# SECUREAUTH LABS. Copyright 2018 SecureAuth Corporation. All rights reserved.
#
# This software is provided under under a slightly modified version
# of the Apache Software License. See the accompanying LICENSE file
# for more information.
#
# Copyright (C) 2001 Michael Teo <michaelteo@bigfoot.com>
# smb.py - SMB/CIFS library
#
# This software is provided 'as-is', without any express or implied warranty.
# In no event will the author be held liable for any damages arising from the
# use of this software.
#
# Permission is granted to anyone to use this software for any purpose,
# including commercial applications, and to alter it and redistribute it
# freely, subject to the following restrictions:
#
# 1. The origin of this software must not be misrepresented; you must not
#    claim that you wrote the original software. If you use this software
#    in a product, an acknowledgment in the product documentation would be
#    appreciated but is not required.
#
# 2. Altered source versions must be plainly marked as such, and must not be
#    misrepresented as being the original software.
#
# 3. This notice cannot be removed or altered from any source distribution.
#
# Altered source done by Alberto Solino (@agsolino)

# Todo:
# [ ] Try [SMB]transport fragmentation using Transact requests
# [ ] Try other methods of doing write (write_raw, transact2, write, write_and_unlock, write_and_close, write_mpx)
# [-] Try replacements for SMB_COM_NT_CREATE_ANDX  (CREATE, T_TRANSACT_CREATE, OPEN_ANDX works
# [x] Fix forceWriteAndx, which needs to send a RecvRequest, because recv() will not send it
# [x] Fix Recv() when using RecvAndx and the answer comes splet in several packets
# [ ] Try [SMB]transport fragmentation with overlapping segments
# [ ] Try [SMB]transport fragmentation with out of order segments
# [x] Do chained AndX requests
# [ ] Transform the rest of the calls to structure
# [X] Implement TRANS/TRANS2 reassembly for list_path
from __future__ import division, print_function

import datetime

# For signing
import hashlib
import os
import socket
from binascii import a2b_hex
from contextlib import contextmanager
from struct import pack, unpack

from pyasn1.type.univ import noValue

from impacket import LOG, nmb, nt_errors, ntlm
from impacket.krb5.gssapi import KRB5_AP_REQ
from impacket.spnego import (
    ASN1_AID,
    ASN1_OID,
    SPNEGO_NegTokenInit,
    SPNEGO_NegTokenResp,
    TypesMech,
    asn1encode,
)
from impacket.structure import Structure

unicode_support = 0
unicode_convert = 1

# Dialect for SMB1
SMB_DIALECT = "NT LM 0.12"

# Shared Device Type
SHARED_DISK = 0x00
SHARED_DISK_HIDDEN = 0x80000000
SHARED_PRINT_QUEUE = 0x01
SHARED_DEVICE = 0x02
SHARED_IPC = 0x03

# Extended attributes mask
ATTR_ARCHIVE = 0x020
ATTR_COMPRESSED = 0x800
ATTR_NORMAL = 0x080
ATTR_HIDDEN = 0x002
ATTR_READONLY = 0x001
ATTR_TEMPORARY = 0x100
ATTR_DIRECTORY = 0x010
ATTR_SYSTEM = 0x004

# Service Type
SERVICE_DISK = "A:"
SERVICE_PRINTER = "LPT1:"
SERVICE_IPC = "IPC"
SERVICE_COMM = "COMM"
SERVICE_ANY = "?????"

# Server Type (Can be used to mask with SMBMachine.get_type() or SMBDomain.get_type())
SV_TYPE_WORKSTATION = 0x00000001
SV_TYPE_SERVER = 0x00000002
SV_TYPE_SQLSERVER = 0x00000004
SV_TYPE_DOMAIN_CTRL = 0x00000008
SV_TYPE_DOMAIN_BAKCTRL = 0x00000010
SV_TYPE_TIME_SOURCE = 0x00000020
SV_TYPE_AFP = 0x00000040
SV_TYPE_NOVELL = 0x00000080
SV_TYPE_DOMAIN_MEMBER = 0x00000100
SV_TYPE_PRINTQ_SERVER = 0x00000200
SV_TYPE_DIALIN_SERVER = 0x00000400
SV_TYPE_XENIX_SERVER = 0x00000800
SV_TYPE_NT = 0x00001000
SV_TYPE_WFW = 0x00002000
SV_TYPE_SERVER_NT = 0x00004000
SV_TYPE_POTENTIAL_BROWSER = 0x00010000
SV_TYPE_BACKUP_BROWSER = 0x00020000
SV_TYPE_MASTER_BROWSER = 0x00040000
SV_TYPE_DOMAIN_MASTER = 0x00080000
SV_TYPE_LOCAL_LIST_ONLY = 0x40000000
SV_TYPE_DOMAIN_ENUM = 0x80000000

# Options values for SMB.stor_file and SMB.retr_file
SMB_O_CREAT = 0x10  # Create the file if file does not exists. Otherwise, operation fails.
SMB_O_EXCL = 0x00  # When used with SMB_O_CREAT, operation fails if file exists. Cannot be used with SMB_O_OPEN.
SMB_O_OPEN = 0x01  # Open the file if the file exists
SMB_O_TRUNC = 0x02  # Truncate the file if the file exists

# Share Access Mode
SMB_SHARE_COMPAT = 0x00
SMB_SHARE_DENY_EXCL = 0x10
SMB_SHARE_DENY_WRITE = 0x20
SMB_SHARE_DENY_READEXEC = 0x30
SMB_SHARE_DENY_NONE = 0x40
SMB_ACCESS_READ = 0x00
SMB_ACCESS_WRITE = 0x01
SMB_ACCESS_READWRITE = 0x02
SMB_ACCESS_EXEC = 0x03

TRANS_DISCONNECT_TID = 1
TRANS_NO_RESPONSE = 2

STATUS_SUCCESS = 0x00000000
STATUS_LOGON_FAILURE = 0xC000006D
STATUS_LOGON_TYPE_NOT_GRANTED = 0xC000015B
MAX_TFRAG_SIZE = 5840
EVASION_NONE = 0
EVASION_LOW = 1
EVASION_HIGH = 2
EVASION_MAX = 3
RPC_X_BAD_STUB_DATA = 0x6F7

# SMB_FILE_ATTRIBUTES

SMB_FILE_ATTRIBUTE_NORMAL = 0x0000
SMB_FILE_ATTRIBUTE_READONLY = 0x0001
SMB_FILE_ATTRIBUTE_HIDDEN = 0x0002
SMB_FILE_ATTRIBUTE_SYSTEM = 0x0004
SMB_FILE_ATTRIBUTE_VOLUME = 0x0008
SMB_FILE_ATTRIBUTE_DIRECTORY = 0x0010
SMB_FILE_ATTRIBUTE_ARCHIVE = 0x0020
SMB_SEARCH_ATTRIBUTE_READONLY = 0x0100
SMB_SEARCH_ATTRIBUTE_HIDDEN = 0x0200
SMB_SEARCH_ATTRIBUTE_SYSTEM = 0x0400
SMB_SEARCH_ATTRIBUTE_DIRECTORY = 0x1000
SMB_SEARCH_ATTRIBUTE_ARCHIVE = 0x2000

# Session SetupAndX Action flags
SMB_SETUP_GUEST = 0x01
SMB_SETUP_USE_LANMAN_KEY = 0x02

# QUERY_INFORMATION levels
SMB_INFO_ALLOCATION = 0x0001
SMB_INFO_VOLUME = 0x0002
FILE_FS_SIZE_INFORMATION = 0x0003
SMB_QUERY_FS_VOLUME_INFO = 0x0102
SMB_QUERY_FS_SIZE_INFO = 0x0103
SMB_QUERY_FILE_EA_INFO = 0x0103
SMB_QUERY_FS_DEVICE_INFO = 0x0104
SMB_QUERY_FS_ATTRIBUTE_INFO = 0x0105
SMB_QUERY_FILE_BASIC_INFO = 0x0101
SMB_QUERY_FILE_STANDARD_INFO = 0x0102
SMB_QUERY_FILE_ALL_INFO = 0x0107
FILE_FS_FULL_SIZE_INFORMATION = 0x03EF

# SET_INFORMATION levels
SMB_SET_FILE_DISPOSITION_INFO = 0x0102
SMB_SET_FILE_BASIC_INFO = 0x0101
SMB_SET_FILE_END_OF_FILE_INFO = 0x0104


# File System Attributes
FILE_CASE_SENSITIVE_SEARCH = 0x00000001
FILE_CASE_PRESERVED_NAMES = 0x00000002
FILE_UNICODE_ON_DISK = 0x00000004
FILE_PERSISTENT_ACLS = 0x00000008
FILE_FILE_COMPRESSION = 0x00000010
FILE_VOLUME_IS_COMPRESSED = 0x00008000

# FIND_FIRST2 flags and levels
SMB_FIND_CLOSE_AFTER_REQUEST = 0x0001
SMB_FIND_CLOSE_AT_EOS = 0x0002
SMB_FIND_RETURN_RESUME_KEYS = 0x0004
SMB_FIND_CONTINUE_FROM_LAST = 0x0008
SMB_FIND_WITH_BACKUP_INTENT = 0x0010

FILE_DIRECTORY_FILE = 0x00000001
FILE_DELETE_ON_CLOSE = 0x00001000
FILE_NON_DIRECTORY_FILE = 0x00000040

SMB_FIND_INFO_STANDARD = 0x0001
SMB_FIND_FILE_DIRECTORY_INFO = 0x0101
SMB_FIND_FILE_FULL_DIRECTORY_INFO = 0x0102
SMB_FIND_FILE_NAMES_INFO = 0x0103
SMB_FIND_FILE_BOTH_DIRECTORY_INFO = 0x0104
SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO = 0x105
SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO = 0x106


# DesiredAccess flags
FILE_READ_DATA = 0x00000001
FILE_WRITE_DATA = 0x00000002
FILE_APPEND_DATA = 0x00000004
FILE_EXECUTE = 0x00000020
MAXIMUM_ALLOWED = 0x02000000
GENERIC_ALL = 0x10000000
GENERIC_EXECUTE = 0x20000000
GENERIC_WRITE = 0x40000000
GENERIC_READ = 0x80000000

# ShareAccess flags
FILE_SHARE_NONE = 0x00000000
FILE_SHARE_READ = 0x00000001
FILE_SHARE_WRITE = 0x00000002
FILE_SHARE_DELETE = 0x00000004

# CreateDisposition flags
FILE_SUPERSEDE = 0x00000000
FILE_OPEN = 0x00000001
FILE_CREATE = 0x00000002
FILE_OPEN_IF = 0x00000003
FILE_OVERWRITE = 0x00000004
FILE_OVERWRITE_IF = 0x00000005


def strerror(errclass, errcode):
    if errclass == 0x01:
        return "OS error", ERRDOS.get(errcode, "Unknown error")
    elif errclass == 0x02:
        return "Server error", ERRSRV.get(errcode, "Unknown error")
    elif errclass == 0x03:
        return "Hardware error", ERRHRD.get(errcode, "Unknown error")
    # This is not a standard error class for SMB
    # elif errclass == 0x80:
    #    return 'Browse error', ERRBROWSE.get(errcode, 'Unknown error')
    elif errclass == 0xFF:
        return "Bad command", "Bad command. Please file bug report"
    else:
        return "Unknown error", "Unknown error"


# Raised when an error has occurred during a session
class SessionError(Exception):
    # SMB X/Open error codes for the ERRDOS error class
    ERRsuccess = 0
    ERRbadfunc = 1
    ERRbadfile = 2
    ERRbadpath = 3
    ERRnofids = 4
    ERRnoaccess = 5
    ERRbadfid = 6
    ERRbadmcb = 7
    ERRnomem = 8
    ERRbadmem = 9
    ERRbadenv = 10
    ERRbadaccess = 12
    ERRbaddata = 13
    ERRres = 14
    ERRbaddrive = 15
    ERRremcd = 16
    ERRdiffdevice = 17
    ERRnofiles = 18
    ERRgeneral = 31
    ERRbadshare = 32
    ERRlock = 33
    ERRunsup = 50
    ERRnetnamedel = 64
    ERRnosuchshare = 67
    ERRfilexists = 80
    ERRinvalidparam = 87
    ERRcannotopen = 110
    ERRinsufficientbuffer = 122
    ERRinvalidname = 123
    ERRunknownlevel = 124
    ERRnotlocked = 158
    ERRrename = 183
    ERRbadpipe = 230
    ERRpipebusy = 231
    ERRpipeclosing = 232
    ERRnotconnected = 233
    ERRmoredata = 234
    ERRnomoreitems = 259
    ERRbaddirectory = 267
    ERReasnotsupported = 282
    ERRlogonfailure = 1326
    ERRbuftoosmall = 2123
    ERRunknownipc = 2142
    ERRnosuchprintjob = 2151
    ERRinvgroup = 2455

    # here's a special one from observing NT
    ERRnoipc = 66

    # These errors seem to be only returned by the NT printer driver system
    ERRdriveralreadyinstalled = 1795
    ERRunknownprinterport = 1796
    ERRunknownprinterdriver = 1797
    ERRunknownprintprocessor = 1798
    ERRinvalidseparatorfile = 1799
    ERRinvalidjobpriority = 1800
    ERRinvalidprintername = 1801
    ERRprinteralreadyexists = 1802
    ERRinvalidprintercommand = 1803
    ERRinvaliddatatype = 1804
    ERRinvalidenvironment = 1805

    ERRunknownprintmonitor = 3000
    ERRprinterdriverinuse = 3001
    ERRspoolfilenotfound = 3002
    ERRnostartdoc = 3003
    ERRnoaddjob = 3004
    ERRprintprocessoralreadyinstalled = 3005
    ERRprintmonitoralreadyinstalled = 3006
    ERRinvalidprintmonitor = 3007
    ERRprintmonitorinuse = 3008
    ERRprinterhasjobsqueued = 3009

    # Error codes for the ERRSRV class

    ERRerror = 1
    ERRbadpw = 2
    ERRbadtype = 3
    ERRaccess = 4
    ERRinvnid = 5
    ERRinvnetname = 6
    ERRinvdevice = 7
    ERRqfull = 49
    ERRqtoobig = 50
    ERRinvpfid = 52
    ERRsmbcmd = 64
    ERRsrverror = 65
    ERRfilespecs = 67
    ERRbadlink = 68
    ERRbadpermits = 69
    ERRbadpid = 70
    ERRsetattrmode = 71
    ERRpaused = 81
    ERRmsgoff = 82
    ERRnoroom = 83
    ERRrmuns = 87
    ERRtimeout = 88
    ERRnoresource = 89
    ERRtoomanyuids = 90
    ERRbaduid = 91
    ERRuseMPX = 250
    ERRuseSTD = 251
    ERRcontMPX = 252
    ERRbadPW = None
    ERRnosupport = 0
    ERRunknownsmb = 22

    # Error codes for the ERRHRD class

    ERRnowrite = 19
    ERRbadunit = 20
    ERRnotready = 21
    ERRbadcmd = 22
    ERRdata = 23
    ERRbadreq = 24
    ERRseek = 25
    ERRbadmedia = 26
    ERRbadsector = 27
    ERRnopaper = 28
    ERRwrite = 29
    ERRread = 30
    ERRwrongdisk = 34
    ERRFCBunavail = 35
    ERRsharebufexc = 36
    ERRdiskfull = 39

    hard_msgs = {
        19: ("ERRnowrite", "Attempt to write on write-protected diskette."),
        20: ("ERRbadunit", "Unknown unit."),
        21: ("ERRnotready", "Drive not ready."),
        22: ("ERRbadcmd", "Unknown command."),
        23: ("ERRdata", "Data error (CRC)."),
        24: ("ERRbadreq", "Bad request structure length."),
        25: ("ERRseek", "Seek error."),
        26: ("ERRbadmedia", "Unknown media type."),
        27: ("ERRbadsector", "Sector not found."),
        28: ("ERRnopaper", "Printer out of paper."),
        29: ("ERRwrite", "Write fault."),
        30: ("ERRread", "Read fault."),
        31: ("ERRgeneral", "General failure."),
        32: ("ERRbadshare", "An open conflicts with an existing open."),
        33: (
            "ERRlock",
            "A Lock request conflicted with an existing lock or specified an invalid mode, or an Unlock requested attempted to remove a lock held by another process.",
        ),
        34: ("ERRwrongdisk", "The wrong disk was found in a drive."),
        35: ("ERRFCBUnavail", "No FCBs are available to process request."),
        36: ("ERRsharebufexc", "A sharing buffer has been exceeded."),
    }

    dos_msgs = {
        ERRbadfunc: ("ERRbadfunc", "Invalid function."),
        ERRbadfile: ("ERRbadfile", "File not found."),
        ERRbadpath: ("ERRbadpath", "Directory invalid."),
        ERRnofids: ("ERRnofids", "No file descriptors available"),
        ERRnoaccess: ("ERRnoaccess", "Access denied."),
        ERRbadfid: ("ERRbadfid", "Invalid file handle."),
        ERRbadmcb: ("ERRbadmcb", "Memory control blocks destroyed."),
        ERRnomem: ("ERRnomem", "Insufficient server memory to perform the requested function."),
        ERRbadmem: ("ERRbadmem", "Invalid memory block address."),
        ERRbadenv: ("ERRbadenv", "Invalid environment."),
        11: ("ERRbadformat", "Invalid format."),
        ERRbadaccess: ("ERRbadaccess", "Invalid open mode."),
        ERRbaddata: ("ERRbaddata", "Invalid data."),
        ERRres: ("ERRres", "reserved."),
        ERRbaddrive: ("ERRbaddrive", "Invalid drive specified."),
        ERRremcd: ("ERRremcd", "A Delete Directory request attempted  to  remove  the  server's  current directory."),
        ERRdiffdevice: ("ERRdiffdevice", "Not same device."),
        ERRnofiles: ("ERRnofiles", "A File Search command can find no more files matching the specified criteria."),
        ERRbadshare: (
            "ERRbadshare",
            "The sharing mode specified for an Open conflicts with existing  FIDs  on the file.",
        ),
        ERRlock: (
            "ERRlock",
            "A Lock request conflicted with an existing lock or specified an  invalid mode,  or an Unlock requested attempted to remove a lock held by another process.",
        ),
        ERRunsup: ("ERRunsup", "The operation is unsupported"),
        ERRnosuchshare: ("ERRnosuchshare", "You specified an invalid share name"),
        ERRfilexists: (
            "ERRfilexists",
            "The file named in a Create Directory, Make  New  File  or  Link  request already exists.",
        ),
        ERRinvalidname: ("ERRinvalidname", "Invalid name"),
        ERRbadpipe: ("ERRbadpipe", "Pipe invalid."),
        ERRpipebusy: ("ERRpipebusy", "All instances of the requested pipe are busy."),
        ERRpipeclosing: ("ERRpipeclosing", "Pipe close in progress."),
        ERRnotconnected: ("ERRnotconnected", "No process on other end of pipe."),
        ERRmoredata: ("ERRmoredata", "There is more data to be returned."),
        ERRinvgroup: ("ERRinvgroup", "Invalid workgroup (try the -W option)"),
        ERRlogonfailure: ("ERRlogonfailure", "Logon failure"),
        ERRdiskfull: ("ERRdiskfull", "Disk full"),
        ERRgeneral: ("ERRgeneral", "General failure"),
        ERRunknownlevel: ("ERRunknownlevel", "Unknown info level"),
    }

    server_msgs = {
        1: ("ERRerror", "Non-specific error code."),
        2: ("ERRbadpw", "Bad password - name/password pair in a Tree Connect or Session Setup are invalid."),
        3: ("ERRbadtype", "reserved."),
        4: (
            "ERRaccess",
            "The requester does not have  the  necessary  access  rights  within  the specified  context for the requested function. The context is defined by the TID or the UID.",
        ),
        5: ("ERRinvnid", "The tree ID (TID) specified in a command was invalid."),
        6: ("ERRinvnetname", "Invalid network name in tree connect."),
        7: (
            "ERRinvdevice",
            "Invalid device - printer request made to non-printer connection or  non-printer request made to printer connection.",
        ),
        49: ("ERRqfull", "Print queue full (files) -- returned by open print file."),
        50: ("ERRqtoobig", "Print queue full -- no space."),
        51: ("ERRqeof", "EOF on print queue dump."),
        52: ("ERRinvpfid", "Invalid print file FID."),
        64: ("ERRsmbcmd", "The server did not recognize the command received."),
        65: ("ERRsrverror", "The server encountered an internal error, e.g., system file unavailable."),
        67: (
            "ERRfilespecs",
            "The file handle (FID) and pathname parameters contained an invalid  combination of values.",
        ),
        68: ("ERRreserved", "reserved."),
        69: (
            "ERRbadpermits",
            "The access permissions specified for a file or directory are not a valid combination.  The server cannot set the requested attribute.",
        ),
        70: ("ERRreserved", "reserved."),
        71: ("ERRsetattrmode", "The attribute mode in the Set File Attribute request is invalid."),
        81: ("ERRpaused", "Server is paused."),
        82: ("ERRmsgoff", "Not receiving messages."),
        83: ("ERRnoroom", "No room to buffer message."),
        87: ("ERRrmuns", "Too many remote user names."),
        88: ("ERRtimeout", "Operation timed out."),
        89: ("ERRnoresource", "No resources currently available for request."),
        90: ("ERRtoomanyuids", "Too many UIDs active on this session."),
        91: ("ERRbaduid", "The UID is not known as a valid ID on this session."),
        250: ("ERRusempx", "Temp unable to support Raw, use MPX mode."),
        251: ("ERRusestd", "Temp unable to support Raw, use standard read/write."),
        252: ("ERRcontmpx", "Continue in MPX mode."),
        253: ("ERRreserved", "reserved."),
        254: ("ERRreserved", "reserved."),
        0xFFFF: ("ERRnosupport", "Function not supported."),
    }
    # Error clases

    ERRDOS = 0x1
    error_classes = {
        0: ("SUCCESS", {}),
        ERRDOS: ("ERRDOS", dos_msgs),
        0x02: ("ERRSRV", server_msgs),
        0x03: ("ERRHRD", hard_msgs),
        0x04: ("ERRXOS", {}),
        0xE1: ("ERRRMX1", {}),
        0xE2: ("ERRRMX2", {}),
        0xE3: ("ERRRMX3", {}),
        0xFF: ("ERRCMD", {}),
    }

    def __init__(self, error_string, error_class, error_code, nt_status=0, packet=0):
        Exception.__init__(self, error_string)
        self.nt_status = nt_status
        self._args = error_string
        if nt_status:
            self.error_class = 0
            self.error_code = (error_code << 16) + error_class
        else:
            self.error_class = error_class
            self.error_code = error_code
        self.packet = packet

    def get_error_class(self):
        return self.error_class

    def get_error_code(self):
        return self.error_code

    def get_error_packet(self):
        return self.packet

    def __str__(self):
        error_class = SessionError.error_classes.get(self.error_class, None)
        if not error_class:
            error_code_str = self.error_code
            error_class_str = self.error_class
        else:
            error_class_str = error_class[0]
            error_code = error_class[1].get(self.error_code, None)
            if not error_code:
                error_code_str = self.error_code
            else:
                error_code_str = "%s(%s)" % error_code

        if self.nt_status:
            return "SMB SessionError: %s(%s)" % nt_errors.ERROR_MESSAGES[self.error_code]
        else:
            # Fall back to the old format
            return "SMB SessionError: class: %s, code: %s" % (error_class_str, error_code_str)


# Raised when an supported feature is present/required in the protocol but is not
# currently supported by pysmb
class UnsupportedFeature(Exception):
    pass


# Contains information about a SMB shared device/service
class SharedDevice:
    def __init__(self, name, share_type, comment):
        self.__name = name
        self.__type = share_type
        self.__comment = comment

    def get_name(self):
        return self.__name

    def get_type(self):
        return self.__type

    def get_comment(self):
        return self.__comment

    def __repr__(self):
        return (
            "<SharedDevice instance: name="
            + self.__name
            + ", type="
            + str(self.__type)
            + ', comment="'
            + self.__comment
            + '">'
        )


# Contains information about the shared file/directory
class SharedFile:
    def __init__(self, ctime, atime, mtime, filesize, allocsize, attribs, shortname, longname):
        self.__ctime = ctime
        self.__atime = atime
        self.__mtime = mtime
        self.__filesize = filesize
        self.__allocsize = allocsize
        self.__attribs = attribs
        try:
            if isinstance(shortname, bytes):
                self.__shortname = shortname[: shortname.index(b"\0")]
            else:
                self.__shortname = shortname[: shortname.index("\0")]
        except (ValueError, TypeError):
            self.__shortname = shortname
        try:
            if isinstance(shortname, bytes):
                self.__longname = longname[: longname.index(b"\0")]
            else:
                self.__longname = longname[: longname.index("\0")]
        except (ValueError, TypeError):
            self.__longname = longname

    def get_ctime(self):
        return self.__ctime

    def get_ctime_epoch(self):
        return self.__convert_smbtime(self.__ctime)

    def get_mtime(self):
        return self.__mtime

    def get_mtime_epoch(self):
        return self.__convert_smbtime(self.__mtime)

    def get_atime(self):
        return self.__atime

    def get_atime_epoch(self):
        return self.__convert_smbtime(self.__atime)

    def get_filesize(self):
        return self.__filesize

    def get_allocsize(self):
        return self.__allocsize

    def get_attributes(self):
        return self.__attribs

    def is_archive(self):
        return self.__attribs & ATTR_ARCHIVE

    def is_compressed(self):
        return self.__attribs & ATTR_COMPRESSED

    def is_normal(self):
        return self.__attribs & ATTR_NORMAL

    def is_hidden(self):
        return self.__attribs & ATTR_HIDDEN

    def is_readonly(self):
        return self.__attribs & ATTR_READONLY

    def is_temporary(self):
        return self.__attribs & ATTR_TEMPORARY

    def is_directory(self):
        return self.__attribs & ATTR_DIRECTORY

    def is_system(self):
        return self.__attribs & ATTR_SYSTEM

    def get_shortname(self):
        return self.__shortname

    def get_longname(self):
        return self.__longname

    def __repr__(self):
        return (
            '<SharedFile instance: shortname="'
            + self.__shortname
            + '", longname="'
            + self.__longname
            + '", filesize='
            + str(self.__filesize)
            + ">"
        )

    @staticmethod
    def __convert_smbtime(t):
        x = t >> 32
        y = t & 0xFFFFFFFF
        geo_cal_offset = 11644473600.0  # = 369.0 * 365.25 * 24 * 60 * 60 - (3.0 * 24 * 60 * 60 + 6.0 * 60 * 60)
        return (x * 4.0 * (1 << 30) + (y & 0xFFF00000)) * 1.0e-7 - geo_cal_offset


# Contain information about a SMB machine
class SMBMachine:
    def __init__(self, nbname, nbt_type, comment):
        self.__nbname = nbname
        self.__type = nbt_type
        self.__comment = comment

    def __repr__(self):
        return (
            '<SMBMachine instance: nbname="'
            + self.__nbname
            + '", type='
            + hex(self.__type)
            + ', comment="'
            + self.__comment
            + '">'
        )


class SMBDomain:
    def __init__(self, nbgroup, domain_type, master_browser):
        self.__nbgroup = nbgroup
        self.__type = domain_type
        self.__master_browser = master_browser

    def __repr__(self):
        return (
            '<SMBDomain instance: nbgroup="'
            + self.__nbgroup
            + '", type='
            + hex(self.__type)
            + ', master browser="'
            + self.__master_browser
            + '">'
        )


# Represents a SMB Packet
class NewSMBPacket(Structure):
    structure = (
        ("Signature", '"\xffSMB'),
        ("Command", "B=0"),
        ("ErrorClass", "B=0"),
        ("_reserved", "B=0"),
        ("ErrorCode", "<H=0"),
        ("Flags1", "B=0"),
        ("Flags2", "<H=0"),
        ("PIDHigh", "<H=0"),
        ("SecurityFeatures", '8s=""'),
        ("Reserved", "<H=0"),
        ("Tid", "<H=0xffff"),
        ("Pid", "<H=0"),
        ("Uid", "<H=0"),
        ("Mid", "<H=0"),
        ("Data", "*:"),
    )

    def __init__(self, **kargs):
        Structure.__init__(self, **kargs)

        if ("Flags2" in self.fields) is False:
            self["Flags2"] = 0
        if ("Flags1" in self.fields) is False:
            self["Flags1"] = 0

        if "data" not in kargs:
            self["Data"] = []

    def addCommand(self, command):
        if len(self["Data"]) == 0:
            self["Command"] = command.command
        else:
            self["Data"][-1]["Parameters"]["AndXCommand"] = command.command
            self["Data"][-1]["Parameters"]["AndXOffset"] = len(self)
        self["Data"].append(command)

    def isMoreData(self):
        return (
            self["Command"] in [SMB.SMB_COM_TRANSACTION, SMB.SMB_COM_READ_ANDX, SMB.SMB_COM_READ_RAW]
            and self["ErrorClass"] == 1
            and self["ErrorCode"] == SessionError.ERRmoredata
        )

    def isMoreProcessingRequired(self):
        return self["ErrorClass"] == 0x16 and self["ErrorCode"] == 0xC000

    def isValidAnswer(self, cmd):
        # this was inside a loop reading more from the net (with recv_packet(None))
        if self["Command"] == cmd:
            if self["ErrorClass"] == 0x00 and self["ErrorCode"] == 0x00:
                return 1
            elif self.isMoreData():
                return 1
            elif self.isMoreProcessingRequired():
                return 1
            raise SessionError(
                "SMB Library Error",
                self["ErrorClass"] + (self["_reserved"] << 8),
                self["ErrorCode"],
                self["Flags2"] & SMB.FLAGS2_NT_STATUS,
                self,
            )
        else:
            raise UnsupportedFeature("Unexpected answer from server: Got %d, Expected %d" % (self["Command"], cmd))


class SMBCommand(Structure):
    structure = (
        ("WordCount", "B=len(Parameters)//2"),
        ("_ParametersLength", "_-Parameters", "WordCount*2"),
        ("Parameters", ":"),  # default set by constructor
        ("ByteCount", "<H-Data"),
        ("Data", ":"),  # default set by constructor
    )

    def __init__(self, commandOrData=None, data=None, **kargs):
        if type(commandOrData) == type(0):
            self.command = commandOrData
        else:
            data = data or commandOrData

        Structure.__init__(self, data=data, **kargs)

        if data is None:
            self["Parameters"] = ""
            self["Data"] = ""


class AsciiOrUnicodeStructure(Structure):
    UnicodeStructure = ()
    AsciiStructure = ()

    def __init__(self, flags=0, **kargs):
        if flags & SMB.FLAGS2_UNICODE:
            self.structure = self.UnicodeStructure
        else:
            self.structure = self.AsciiStructure
        Structure.__init__(self, **kargs)


class SMBCommand_Parameters(Structure):
    pass


class SMBAndXCommand_Parameters(Structure):
    commonHdr = (
        ("AndXCommand", "B=0xff"),
        ("_reserved", "B=0"),
        ("AndXOffset", "<H=0"),
    )
    structure = (("Data", ':=""'),)  # default structure, overridden by subclasses


############# TRANSACTIONS RELATED
# TRANS2_QUERY_FS_INFORMATION
# QUERY_FS Information Levels
# SMB_QUERY_FS_ATTRIBUTE_INFO
class SMBQueryFsAttributeInfo(Structure):
    structure = (
        ("FileSystemAttributes", "<L"),
        ("MaxFilenNameLengthInBytes", "<L"),
        ("LengthOfFileSystemName", "<L-FileSystemName"),
        ("FileSystemName", ":"),
    )


class SMBQueryFsInfoVolume(AsciiOrUnicodeStructure):
    commonHdr = (
        ("ulVolSerialNbr", "<L=0xABCDEFAA"),
        ("cCharCount", "<B-VolumeLabel"),
    )
    AsciiStructure = (("VolumeLabel", "z"),)
    UnicodeStructure = (("VolumeLabel", "u"),)


# FILE_FS_SIZE_INFORMATION
class FileFsSizeInformation(Structure):
    structure = (
        ("TotalAllocationUnits", "<q=148529400"),
        ("AvailableAllocationUnits", "<q=14851044"),
        ("SectorsPerAllocationUnit", "<L=2"),
        ("BytesPerSector", "<L=512"),
    )


# SMB_QUERY_FS_SIZE_INFO
class SMBQueryFsSizeInfo(Structure):
    structure = (
        ("TotalAllocationUnits", "<q=148529400"),
        ("TotalFreeAllocationUnits", "<q=14851044"),
        ("SectorsPerAllocationUnit", "<L=2"),
        ("BytesPerSector", "<L=512"),
    )


# FILE_FS_FULL_SIZE_INFORMATION
class SMBFileFsFullSizeInformation(Structure):
    structure = (
        ("TotalAllocationUnits", "<q=148529400"),
        ("CallerAvailableAllocationUnits", "<q=148529400"),
        ("ActualAvailableAllocationUnits", "<q=148529400"),
        ("SectorsPerAllocationUnit", "<L=15"),
        ("BytesPerSector", "<L=512"),
    )


# SMB_QUERY_FS_VOLUME_INFO
class SMBQueryFsVolumeInfo(Structure):
    structure = (
        ("VolumeCreationTime", "<q"),
        ("SerialNumber", "<L=0xABCDEFAA"),
        ("VolumeLabelSize", "<L=len(VolumeLabel)"),
        ("Reserved", "<H=0x10"),
        ("VolumeLabel", ":"),
    )


# SMB_FIND_FILE_BOTH_DIRECTORY_INFO level
class SMBFindFileBothDirectoryInfo(AsciiOrUnicodeStructure):
    commonHdr = (
        ("NextEntryOffset", "<L=0"),
        ("FileIndex", "<L=0"),
        ("CreationTime", "<q"),
        ("LastAccessTime", "<q"),
        ("LastWriteTime", "<q"),
        ("LastChangeTime", "<q"),
        ("EndOfFile", "<q=0"),
        ("AllocationSize", "<q=0"),
        ("ExtFileAttributes", "<L=0"),
    )
    AsciiStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)"),
        ("EaSize", "<L=0"),
        ("ShortNameLength", "<B=0"),
        ("Reserved", "<B=0"),
        ("ShortName", "24s"),
        ("FileName", ":"),
    )
    UnicodeStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)*2"),
        ("EaSize", "<L=0"),
        ("ShortNameLength", "<B=0"),
        ("Reserved", "<B=0"),
        ("ShortName", "24s"),
        ("FileName", ":"),
    )


# SMB_FIND_FILE_ID_FULL_DIRECTORY_INFO level
class SMBFindFileIdFullDirectoryInfo(AsciiOrUnicodeStructure):
    commonHdr = (
        ("NextEntryOffset", "<L=0"),
        ("FileIndex", "<L=0"),
        ("CreationTime", "<q"),
        ("LastAccessTime", "<q"),
        ("LastWriteTime", "<q"),
        ("LastChangeTime", "<q"),
        ("EndOfFile", "<q=0"),
        ("AllocationSize", "<q=0"),
        ("ExtFileAttributes", "<L=0"),
    )
    AsciiStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)"),
        ("EaSize", "<L=0"),
        ("FileID", "<q=0"),
        ("FileName", ":"),
    )
    UnicodeStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)*2"),
        ("EaSize", "<L=0"),
        ("FileID", "<q=0"),
        ("FileName", ":"),
    )


# SMB_FIND_FILE_ID_BOTH_DIRECTORY_INFO level
class SMBFindFileIdBothDirectoryInfo(AsciiOrUnicodeStructure):
    commonHdr = (
        ("NextEntryOffset", "<L=0"),
        ("FileIndex", "<L=0"),
        ("CreationTime", "<q"),
        ("LastAccessTime", "<q"),
        ("LastWriteTime", "<q"),
        ("LastChangeTime", "<q"),
        ("EndOfFile", "<q=0"),
        ("AllocationSize", "<q=0"),
        ("ExtFileAttributes", "<L=0"),
    )
    AsciiStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)"),
        ("EaSize", "<L=0"),
        ("ShortNameLength", "<B=0"),
        ("Reserved", "<B=0"),
        ("ShortName", "24s"),
        ("Reserved", "<H=0"),
        ("FileID", "<q=0"),
        ("FileName", "z"),
    )
    UnicodeStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)*2"),
        ("EaSize", "<L=0"),
        ("ShortNameLength", "<B=0"),
        ("Reserved", "<B=0"),
        ("ShortName", "24s"),
        ("Reserved", "<H=0"),
        ("FileID", "<q=0"),
        ("FileName", ":"),
    )


# SMB_FIND_FILE_DIRECTORY_INFO level
class SMBFindFileDirectoryInfo(AsciiOrUnicodeStructure):
    commonHdr = (
        ("NextEntryOffset", "<L=0"),
        ("FileIndex", "<L=0"),
        ("CreationTime", "<q"),
        ("LastAccessTime", "<q"),
        ("LastWriteTime", "<q"),
        ("LastChangeTime", "<q"),
        ("EndOfFile", "<q=0"),
        ("AllocationSize", "<q=1"),
        ("ExtFileAttributes", "<L=0"),
    )
    AsciiStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)"),
        ("FileName", "z"),
    )
    UnicodeStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)*2"),
        ("FileName", ":"),
    )


# SMB_FIND_FILE_NAMES_INFO level
class SMBFindFileNamesInfo(AsciiOrUnicodeStructure):
    commonHdr = (
        ("NextEntryOffset", "<L=0"),
        ("FileIndex", "<L=0"),
    )
    AsciiStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)"),
        ("FileName", "z"),
    )
    UnicodeStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)*2"),
        ("FileName", ":"),
    )


# SMB_FIND_FILE_FULL_DIRECTORY_INFO level
class SMBFindFileFullDirectoryInfo(AsciiOrUnicodeStructure):
    commonHdr = (
        ("NextEntryOffset", "<L=0"),
        ("FileIndex", "<L=0"),
        ("CreationTime", "<q"),
        ("LastAccessTime", "<q"),
        ("LastWriteTime", "<q"),
        ("LastChangeTime", "<q"),
        ("EndOfFile", "<q=0"),
        ("AllocationSize", "<q=1"),
        ("ExtFileAttributes", "<L=0"),
    )
    AsciiStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)"),
        ("EaSize", "<L"),
        ("FileName", "z"),
    )
    UnicodeStructure = (
        ("FileNameLength", "<L-FileName", "len(FileName)*2"),
        ("EaSize", "<L"),
        ("FileName", ":"),
    )


# SMB_FIND_INFO_STANDARD level
class SMBFindInfoStandard(AsciiOrUnicodeStructure):
    commonHdr = (
        ("ResumeKey", "<L=0xff"),
        ("CreationDate", "<H=0"),
        ("CreationTime", "<H=0"),
        ("LastAccessDate", "<H=0"),
        ("LastAccessTime", "<H=0"),
        ("LastWriteDate", "<H=0"),
        ("LastWriteTime", "<H=0"),
        ("EaSize", "<L"),
        ("AllocationSize", "<L=1"),
        ("ExtFileAttributes", "<H=0"),
    )
    AsciiStructure = (
        ("FileNameLength", "<B-FileName", "len(FileName)"),
        ("FileName", "z"),
    )
    UnicodeStructure = (
        ("FileNameLength", "<B-FileName", "len(FileName)*2"),
        ("FileName", ":"),
    )


# SET_FILE_INFORMATION structures
# SMB_SET_FILE_DISPOSITION_INFO
class SMBSetFileDispositionInfo(Structure):
    structure = (("DeletePending", "<B"),)


# SMB_SET_FILE_BASIC_INFO
class SMBSetFileBasicInfo(Structure):
    structure = (
        ("CreationTime", "<q"),
        ("LastAccessTime", "<q"),
        ("LastWriteTime", "<q"),
        ("ChangeTime", "<q"),
        ("ExtFileAttributes", "<H"),
        ("Reserved", "<L"),
    )


# FILE_STREAM_INFORMATION
class SMBFileStreamInformation(Structure):
    commonHdr = (
        ("NextEntryOffset", "<L=0"),
        ("StreamNameLength", "<L=0"),
        ("StreamSize", "<q=0"),
        ("StreamAllocationSize", "<q=0"),
        ("StreamName", ':=""'),
    )


# FILE_NETWORK_OPEN_INFORMATION
class SMBFileNetworkOpenInfo(Structure):
    structure = (
        ("CreationTime", "<q=0"),
        ("LastAccessTime", "<q=0"),
        ("LastWriteTime", "<q=0"),
        ("ChangeTime", "<q=0"),
        ("AllocationSize", "<q=0"),
        ("EndOfFile", "<q=0"),
        ("FileAttributes", "<L=0"),
        ("Reserved", "<L=0"),
    )


# SMB_SET_FILE_END_OF_FILE_INFO
class SMBSetFileEndOfFileInfo(Structure):
    structure = (("EndOfFile", "<q"),)


# TRANS2_FIND_NEXT2
class SMBFindNext2_Parameters(AsciiOrUnicodeStructure):
    commonHdr = (
        ("SID", "<H"),
        ("SearchCount", "<H"),
        ("InformationLevel", "<H"),
        ("ResumeKey", "<L"),
        ("Flags", "<H"),
    )
    AsciiStructure = (("FileName", "z"),)
    UnicodeStructure = (("FileName", "u"),)


class SMBFindNext2Response_Parameters(Structure):
    structure = (
        ("SearchCount", "<H"),
        ("EndOfSearch", "<H=1"),
        ("EaErrorOffset", "<H=0"),
        ("LastNameOffset", "<H=0"),
    )


class SMBFindNext2_Data(Structure):
    structure = (
        ("GetExtendedAttributesListLength", "_-GetExtendedAttributesList", 'self["GetExtendedAttributesListLength"]'),
        ("GetExtendedAttributesList", ":"),
    )


# TRANS2_FIND_FIRST2
class SMBFindFirst2Response_Parameters(Structure):
    structure = (
        ("SID", "<H"),
        ("SearchCount", "<H"),
        ("EndOfSearch", "<H=1"),
        ("EaErrorOffset", "<H=0"),
        ("LastNameOffset", "<H=0"),
    )


class SMBFindFirst2_Parameters(AsciiOrUnicodeStructure):
    commonHdr = (
        ("SearchAttributes", "<H"),
        ("SearchCount", "<H"),
        ("Flags", "<H"),
        ("InformationLevel", "<H"),
        ("SearchStorageType", "<L"),
    )
    AsciiStructure = (("FileName", "z"),)
    UnicodeStructure = (("FileName", "u"),)


class SMBFindFirst2_Data(Structure):
    structure = (
        ("GetExtendedAttributesListLength", "_-GetExtendedAttributesList", 'self["GetExtendedAttributesListLength"]'),
        ("GetExtendedAttributesList", ":"),
    )


# TRANS2_SET_PATH_INFORMATION
class SMBSetPathInformation_Parameters(AsciiOrUnicodeStructure):
    commonHdr = (
        ("InformationLevel", "<H"),
        ("Reserved", "<L"),
    )
    AsciiStructure = (("FileName", "z"),)
    UnicodeStructure = (("FileName", "u"),)


class SMBSetPathInformationResponse_Parameters(Structure):
    structure = (("EaErrorOffset", "<H=0"),)


# TRANS2_SET_FILE_INFORMATION
class SMBSetFileInformation_Parameters(Structure):
    structure = (
        ("FID", "<H"),
        ("InformationLevel", "<H"),
        ("Reserved", "<H"),
    )


class SMBSetFileInformationResponse_Parameters(Structure):
    structure = (("EaErrorOffset", "<H=0"),)


# TRANS2_QUERY_FILE_INFORMATION
class SMBQueryFileInformation_Parameters(Structure):
    structure = (
        ("FID", "<H"),
        ("InformationLevel", "<H"),
    )


class SMBQueryFileInformationResponse_Parameters(Structure):
    structure = (("EaErrorOffset", "<H=0"),)


class SMBQueryFileInformation_Data(Structure):
    structure = (("GetExtendedAttributeList", ":"),)


# TRANS2_QUERY_PATH_INFORMATION
class SMBQueryPathInformationResponse_Parameters(Structure):
    structure = (("EaErrorOffset", "<H=0"),)


class SMBQueryPathInformation_Parameters(AsciiOrUnicodeStructure):
    commonHdr = (
        ("InformationLevel", "<H"),
        ("Reserved", "<L=0"),
    )
    AsciiStructure = (("FileName", "z"),)
    UnicodeStructure = (("FileName", "u"),)


class SMBQueryPathInformation_Data(Structure):
    structure = (("GetExtendedAttributeList", ":"),)


# SMB_QUERY_FILE_EA_INFO
class SMBQueryFileEaInfo(Structure):
    structure = (("EaSize", "<L=0"),)


# SMB_QUERY_FILE_BASIC_INFO
class SMBQueryFileBasicInfo(Structure):
    structure = (
        ("CreationTime", "<q"),
        ("LastAccessTime", "<q"),
        ("LastWriteTime", "<q"),
        ("LastChangeTime", "<q"),
        ("ExtFileAttributes", "<L"),
        # ('Reserved','<L=0'),
    )


# SMB_QUERY_FILE_STANDARD_INFO
class SMBQueryFileStandardInfo(Structure):
    structure = (
        ("AllocationSize", "<q"),
        ("EndOfFile", "<q"),
        ("NumberOfLinks", "<L=0"),
        ("DeletePending", "<B=0"),
        ("Directory", "<B"),
    )


# SMB_QUERY_FILE_ALL_INFO
class SMBQueryFileAllInfo(Structure):
    structure = (
        ("CreationTime", "<q"),
        ("LastAccessTime", "<q"),
        ("LastWriteTime", "<q"),
        ("LastChangeTime", "<q"),
        ("ExtFileAttributes", "<L"),
        ("Reserved", "<L=0"),
        ("AllocationSize", "<q"),
        ("EndOfFile", "<q"),
        ("NumberOfLinks", "<L=0"),
        ("DeletePending", "<B=0"),
        ("Directory", "<B"),
        ("Reserved", "<H=0"),
        ("EaSize", "<L=0"),
        ("FileNameLength", "<L-FileName", "len(FileName)"),
        ("FileName", ":"),
    )


# \PIPE\LANMAN NetShareEnum
class SMBNetShareEnum(Structure):
    structure = (
        ("RAPOpcode", "<H=0"),
        ("ParamDesc", "z"),
        ("DataDesc", "z"),
        ("InfoLevel", "<H"),
        ("ReceiveBufferSize", "<H"),
    )


class SMBNetShareEnumResponse(Structure):
    structure = (
        ("Status", "<H=0"),
        ("Convert", "<H=0"),
        ("EntriesReturned", "<H"),
        ("EntriesAvailable", "<H"),
    )


class NetShareInfo1(Structure):
    structure = (
        ("NetworkName", "13s"),
        ("Pad", "<B=0"),
        ("Type", "<H=0"),
        ("RemarkOffsetLow", "<H=0"),
        ("RemarkOffsetHigh", "<H=0"),
    )


# \PIPE\LANMAN NetServerGetInfo
class SMBNetServerGetInfoResponse(Structure):
    structure = (
        ("Status", "<H=0"),
        ("Convert", "<H=0"),
        ("TotalBytesAvailable", "<H"),
    )


class SMBNetServerInfo1(Structure):
    # Level 1 Response
    structure = (
        ("ServerName", "16s"),
        ("MajorVersion", "B=5"),
        ("MinorVersion", "B=0"),
        ("ServerType", "<L=3"),
        ("ServerCommentLow", "<H=0"),
        ("ServerCommentHigh", "<H=0"),
    )


# \PIPE\LANMAN NetShareGetInfo
class SMBNetShareGetInfo(Structure):
    structure = (
        ("RAPOpcode", "<H=0"),
        ("ParamDesc", "z"),
        ("DataDesc", "z"),
        ("ShareName", "z"),
        ("InfoLevel", "<H"),
        ("ReceiveBufferSize", "<H"),
    )


class SMBNetShareGetInfoResponse(Structure):
    structure = (
        ("Status", "<H=0"),
        ("Convert", "<H=0"),
        ("TotalBytesAvailable", "<H"),
    )


############# Security Features
class SecurityFeatures(Structure):
    structure = (
        ("Key", "<L=0"),
        ("CID", "<H=0"),
        ("SequenceNumber", "<H=0"),
    )


############# SMB_COM_QUERY_INFORMATION2 (0x23)
class SMBQueryInformation2_Parameters(Structure):
    structure = (("Fid", "<H"),)


class SMBQueryInformation2Response_Parameters(Structure):
    structure = (
        ("CreateDate", "<H"),
        ("CreationTime", "<H"),
        ("LastAccessDate", "<H"),
        ("LastAccessTime", "<H"),
        ("LastWriteDate", "<H"),
        ("LastWriteTime", "<H"),
        ("FileDataSize", "<L"),
        ("FileAllocationSize", "<L"),
        ("FileAttributes", "<L"),
    )


############# SMB_COM_SESSION_SETUP_ANDX (0x73)
class SMBSessionSetupAndX_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("MaxBuffer", "<H"),
        ("MaxMpxCount", "<H"),
        ("VCNumber", "<H"),
        ("SessionKey", "<L"),
        ("AnsiPwdLength", "<H"),
        ("UnicodePwdLength", "<H"),
        ("_reserved", "<L=0"),
        ("Capabilities", "<L"),
    )


class SMBSessionSetupAndX_Extended_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("MaxBufferSize", "<H"),
        ("MaxMpxCount", "<H"),
        ("VcNumber", "<H"),
        ("SessionKey", "<L"),
        ("SecurityBlobLength", "<H"),
        ("Reserved", "<L=0"),
        ("Capabilities", "<L"),
    )


class SMBSessionSetupAndX_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("AnsiPwdLength", "_-AnsiPwd", 'self["AnsiPwdLength"]'),
        ("UnicodePwdLength", "_-UnicodePwd", 'self["UnicodePwdLength"]'),
        ("AnsiPwd", ':=""'),
        ("UnicodePwd", ':=""'),
        ("Account", 'z=""'),
        ("PrimaryDomain", 'z=""'),
        ("NativeOS", 'z=""'),
        ("NativeLanMan", 'z=""'),
    )

    UnicodeStructure = (
        ("AnsiPwdLength", "_-AnsiPwd", 'self["AnsiPwdLength"]'),
        ("UnicodePwdLength", "_-UnicodePwd", 'self["UnicodePwdLength"]'),
        ("AnsiPwd", ':=""'),
        ("UnicodePwd", ':=""'),
        ("Account", 'u=""'),
        ("PrimaryDomain", 'u=""'),
        ("NativeOS", 'u=""'),
        ("NativeLanMan", 'u=""'),
    )


class SMBSessionSetupAndX_Extended_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("SecurityBlobLength", "_-SecurityBlob", 'self["SecurityBlobLength"]'),
        ("SecurityBlob", ":"),
        ("NativeOS", 'z=""'),
        ("NativeLanMan", 'z=""'),
    )

    UnicodeStructure = (
        ("SecurityBlobLength", "_-SecurityBlob", 'self["SecurityBlobLength"]'),
        ("SecurityBlob", ":"),
        ("NativeOS", 'u=""'),
        ("NativeLanMan", 'u=""'),
    )


class SMBSessionSetupAndXResponse_Parameters(SMBAndXCommand_Parameters):
    structure = (("Action", "<H"),)


class SMBSessionSetupAndX_Extended_Response_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("Action", "<H=0"),
        ("SecurityBlobLength", "<H"),
    )


class SMBSessionSetupAndXResponse_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("NativeOS", 'z=""'),
        ("NativeLanMan", 'z=""'),
        ("PrimaryDomain", 'z=""'),
    )

    UnicodeStructure = (
        ("NativeOS", 'u=""'),
        ("NativeLanMan", 'u=""'),
        ("PrimaryDomain", 'u=""'),
    )


class SMBSessionSetupAndX_Extended_Response_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("SecurityBlobLength", "_-SecurityBlob", 'self["SecurityBlobLength"]'),
        ("SecurityBlob", ":"),
        ("NativeOS", 'z=""'),
        ("NativeLanMan", 'z=""'),
    )

    UnicodeStructure = (
        ("SecurityBlobLength", "_-SecurityBlob", 'self["SecurityBlobLength"]'),
        ("SecurityBlob", ":"),
        ("PadLen", "_-Pad", '1 if (len(self["SecurityBlob"]) % 2 == 0) else 0'),
        ("Pad", ':=""'),
        ("NativeOS", 'u=""'),
        ("NativeLanMan", 'u=""'),
    )

    def getData(self):
        if self.structure == self.UnicodeStructure:
            if len(str(self["SecurityBlob"])) % 2 == 0:
                self["Pad"] = "\x00"
        return AsciiOrUnicodeStructure.getData(self)


############# SMB_COM_TREE_CONNECT (0x70)
class SMBTreeConnect_Parameters(SMBCommand_Parameters):
    structure = ()


class SMBTreeConnect_Data(SMBCommand_Parameters):
    structure = (
        ("PathFormat", '"\x04'),
        ("Path", "z"),
        ("PasswordFormat", '"\x04'),
        ("Password", "z"),
        ("ServiceFormat", '"\x04'),
        ("Service", "z"),
    )


############# SMB_COM_TREE_CONNECT_ANDX (0x75)
class SMBTreeConnectAndX_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("Flags", "<H=0"),
        ("PasswordLength", "<H"),
    )


class SMBTreeConnectAndXResponse_Parameters(SMBAndXCommand_Parameters):
    structure = (("OptionalSupport", "<H=0"),)


class SMBTreeConnectAndXExtendedResponse_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("OptionalSupport", "<H=1"),
        ("MaximalShareAccessRights", "<L=0x1fffff"),
        ("GuestMaximalShareAccessRights", "<L=0x1fffff"),
    )


class SMBTreeConnectAndX_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("_PasswordLength", "_-Password", 'self["_PasswordLength"]'),
        ("Password", ":"),
        ("Path", "z"),
        ("Service", "z"),
    )

    UnicodeStructure = (
        ("_PasswordLength", "_-Password", 'self["_PasswordLength"] if self["_PasswordLength"] > 0 else 1'),
        ("Password", ":"),
        ("Path", "u"),
        ("Service", "z"),
    )


class SMBTreeConnectAndXResponse_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("Service", "z"),
        ("PadLen", "_-Pad", 'self["PadLen"]'),
        ("Pad", ':=""'),
        ("NativeFileSystem", "z"),
    )
    UnicodeStructure = (
        ("Service", "z"),
        ("PadLen", "_-Pad", 'self["PadLen"]'),
        ("Pad", ':=""'),
        ("NativeFileSystem", "u"),
    )


############# SMB_COM_NT_CREATE_ANDX (0xA2)
class SMBNtCreateAndX_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("_reserved", "B=0"),
        ("FileNameLength", "<H"),  # NameLength
        ("CreateFlags", "<L"),  # Flags
        ("RootFid", "<L=0"),  # RootDirectoryFID
        ("AccessMask", "<L"),  # DesiredAccess
        ("AllocationSizeLo", "<L=0"),  # AllocationSize
        ("AllocationSizeHi", "<L=0"),
        ("FileAttributes", "<L=0"),  # ExtFileAttributes
        ("ShareAccess", "<L=3"),  #
        ("Disposition", "<L=1"),  # CreateDisposition
        ("CreateOptions", "<L"),  # CreateOptions
        ("Impersonation", "<L=2"),
        ("SecurityFlags", "B=3"),
    )


class SMBNtCreateAndXResponse_Parameters(SMBAndXCommand_Parameters):
    # XXX Is there a memory leak in the response for NTCreate (where the Data section would be) in Win 2000, Win XP, and Win 2003?
    structure = (
        ("OplockLevel", "B=0"),
        ("Fid", "<H"),
        ("CreateAction", "<L"),
        ("CreateTime", "<q=0"),
        ("LastAccessTime", "<q=0"),
        ("LastWriteTime", "<q=0"),
        ("LastChangeTime", "<q=0"),
        ("FileAttributes", "<L=0x80"),
        ("AllocationSize", "<q=0"),
        ("EndOfFile", "<q=0"),
        ("FileType", "<H=0"),
        ("IPCState", "<H=0"),
        ("IsDirectory", "B"),
    )


class SMBNtCreateAndXExtendedResponse_Parameters(SMBAndXCommand_Parameters):
    # [MS-SMB] Extended response description
    structure = (
        ("OplockLevel", "B=0"),
        ("Fid", "<H"),
        ("CreateAction", "<L"),
        ("CreateTime", "<q=0"),
        ("LastAccessTime", "<q=0"),
        ("LastWriteTime", "<q=0"),
        ("LastChangeTime", "<q=0"),
        ("FileAttributes", "<L=0x80"),
        ("AllocationSize", "<q=0"),
        ("EndOfFile", "<q=0"),
        ("FileType", "<H=0"),
        ("IPCState", "<H=0"),
        ("IsDirectory", "B"),
        ("VolumeGUID", "16s"),
        ("FileIdLow", "<L=0"),
        ("FileIdHigh", "<L=0"),
        ("MaximalAccessRights", "<L=0x12019b"),
        ("GuestMaximalAccessRights", "<L=0x120089"),
    )


class SMBNtCreateAndX_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (("FileName", "z"),)
    UnicodeStructure = (
        ("Pad", "B"),
        ("FileName", "u"),
    )


############# SMB_COM_OPEN_ANDX (0xD2)
class SMBOpenAndX_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("Flags", "<H=0"),
        ("DesiredAccess", "<H=0"),
        ("SearchAttributes", "<H=0"),
        ("FileAttributes", "<H=0"),
        ("CreationTime", "<L=0"),
        ("OpenMode", "<H=1"),  # SMB_O_OPEN = 1
        ("AllocationSize", "<L=0"),
        ("Reserved", '8s=""'),
    )


class SMBOpenAndX_Data(SMBNtCreateAndX_Data):
    pass


class SMBOpenAndXResponse_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("Fid", "<H=0"),
        ("FileAttributes", "<H=0"),
        ("LastWriten", "<L=0"),
        ("FileSize", "<L=0"),
        ("GrantedAccess", "<H=0"),
        ("FileType", "<H=0"),
        ("IPCState", "<H=0"),
        ("Action", "<H=0"),
        ("ServerFid", "<L=0"),
        ("_reserved", "<H=0"),
    )


############# SMB_COM_WRITE (0x0B)
class SMBWrite_Parameters(SMBCommand_Parameters):
    structure = (
        ("Fid", "<H"),
        ("Count", "<H"),
        ("Offset", "<L"),
        ("Remaining", "<H"),
    )


class SMBWriteResponse_Parameters(SMBCommand_Parameters):
    structure = (("Count", "<H"),)


class SMBWrite_Data(Structure):
    structure = (
        ("BufferFormat", "<B=1"),
        ("DataLength", "<H-Data"),
        ("Data", ":"),
    )


############# SMB_COM_WRITE_ANDX (0x2F)
class SMBWriteAndX_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("Fid", "<H=0"),
        ("Offset", "<L=0"),
        ("_reserved", "<L=0xff"),
        ("WriteMode", "<H=8"),
        ("Remaining", "<H=0"),
        ("DataLength_Hi", "<H=0"),
        ("DataLength", "<H=0"),
        ("DataOffset", "<H=0"),
        ("HighOffset", "<L=0"),
    )


class SMBWriteAndX_Data_Short(Structure):
    structure = (
        ("_PadLen", "_-Pad", 'self["DataOffset"] - 59'),
        ("Pad", ":"),
        # ('Pad','<B=0'),
        ("DataLength", "_-Data", 'self["DataLength"]'),
        ("Data", ":"),
    )


class SMBWriteAndX_Data(Structure):
    structure = (
        ("_PadLen", "_-Pad", 'self["DataOffset"] - 63'),
        ("Pad", ":"),
        # ('Pad','<B=0'),
        ("DataLength", "_-Data", 'self["DataLength"]'),
        ("Data", ":"),
    )


class SMBWriteAndX_Parameters_Short(SMBAndXCommand_Parameters):
    structure = (
        ("Fid", "<H"),
        ("Offset", "<L"),
        ("_reserved", "<L=0xff"),
        ("WriteMode", "<H=8"),
        ("Remaining", "<H"),
        ("DataLength_Hi", "<H=0"),
        ("DataLength", "<H"),
        ("DataOffset", "<H=0"),
    )


class SMBWriteAndXResponse_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("Count", "<H"),
        ("Available", "<H"),
        ("Reserved", "<L=0"),
    )


############# SMB_COM_WRITE_RAW (0x1D)
class SMBWriteRaw_Parameters(SMBCommand_Parameters):
    structure = (
        ("Fid", "<H"),
        ("Count", "<H"),
        ("_reserved", "<H=0"),
        ("Offset", "<L"),
        ("Timeout", "<L=0"),
        ("WriteMode", "<H=0"),
        ("_reserved2", "<L=0"),
        ("DataLength", "<H"),
        ("DataOffset", "<H=0"),
    )


############# SMB_COM_READ (0x0A)
class SMBRead_Parameters(SMBCommand_Parameters):
    structure = (
        ("Fid", "<H"),
        ("Count", "<H"),
        ("Offset", "<L"),
        ("Remaining", "<H=Count"),
    )


class SMBReadResponse_Parameters(Structure):
    structure = (
        ("Count", "<H=0"),
        ("_reserved", '8s=""'),
    )


class SMBReadResponse_Data(Structure):
    structure = (
        ("BufferFormat", "<B=0x1"),
        ("DataLength", "<H-Data"),
        ("Data", ":"),
    )


############# SMB_COM_READ_RAW (0x1A)
class SMBReadRaw_Parameters(SMBCommand_Parameters):
    structure = (
        ("Fid", "<H"),
        ("Offset", "<L"),
        ("MaxCount", "<H"),
        ("MinCount", "<H=MaxCount"),
        ("Timeout", "<L=0"),
        ("_reserved", "<H=0"),
    )


############# SMB_COM_NT_TRANSACT  (0xA0)
class SMBNTTransaction_Parameters(SMBCommand_Parameters):
    structure = (
        ("MaxSetupCount", "<B=0"),
        ("Reserved1", "<H=0"),
        ("TotalParameterCount", "<L"),
        ("TotalDataCount", "<L"),
        ("MaxParameterCount", "<L=1024"),
        ("MaxDataCount", "<L=65504"),
        ("ParameterCount", "<L"),
        ("ParameterOffset", "<L"),
        ("DataCount", "<L"),
        ("DataOffset", "<L"),
        ("SetupCount", "<B=len(Setup)//2"),
        ("Function", "<H=0"),
        ("SetupLength", "_-Setup", "SetupCount*2"),
        ("Setup", ":"),
    )


class SMBNTTransactionResponse_Parameters(SMBCommand_Parameters):
    structure = (
        ("Reserved1", '3s=""'),
        ("TotalParameterCount", "<L"),
        ("TotalDataCount", "<L"),
        ("ParameterCount", "<L"),
        ("ParameterOffset", "<L"),
        ("ParameterDisplacement", "<L=0"),
        ("DataCount", "<L"),
        ("DataOffset", "<L"),
        ("DataDisplacement", "<L=0"),
        ("SetupCount", "<B=0"),
        ("SetupLength", "_-Setup", "SetupCount*2"),
        ("Setup", ":"),
    )


class SMBNTTransaction_Data(Structure):
    structure = (
        ("Pad1Length", "_-Pad1", 'self["Pad1Length"]'),
        ("Pad1", ":"),
        ("NT_Trans_ParametersLength", "_-NT_Trans_Parameters", 'self["NT_Trans_ParametersLength"]'),
        ("NT_Trans_Parameters", ":"),
        ("Pad2Length", "_-Pad2", 'self["Pad2Length"]'),
        ("Pad2", ":"),
        ("NT_Trans_DataLength", "_-NT_Trans_Data", 'self["NT_Trans_DataLength"]'),
        ("NT_Trans_Data", ":"),
    )


class SMBNTTransactionResponse_Data(Structure):
    structure = (
        ("Pad1Length", "_-Pad1", 'self["Pad1Length"]'),
        ("Pad1", ":"),
        ("Trans_ParametersLength", "_-Trans_Parameters", 'self["Trans_ParametersLength"]'),
        ("Trans_Parameters", ":"),
        ("Pad2Length", "_-Pad2", 'self["Pad2Length"]'),
        ("Pad2", ":"),
        ("Trans_DataLength", "_-Trans_Data", 'self["Trans_DataLength"]'),
        ("Trans_Data", ":"),
    )


############# SMB_COM_TRANSACTION2_SECONDARY (0x33)
class SMBTransaction2Secondary_Parameters(SMBCommand_Parameters):
    structure = (
        ("TotalParameterCount", "<H"),
        ("TotalDataCount", "<H"),
        ("ParameterCount", "<H"),
        ("ParameterOffset", "<H"),
        ("ParameterDisplacement", "<H"),
        ("DataCount", "<H"),
        ("DataOffset", "<H"),
        ("DataDisplacement", "<H=0"),
        ("FID", "<H"),
    )


class SMBTransaction2Secondary_Data(Structure):
    structure = (
        ("Pad1Length", "_-Pad1", 'self["Pad1Length"]'),
        ("Pad1", ":"),
        ("Trans_ParametersLength", "_-Trans_Parameters", 'self["Trans_ParametersLength"]'),
        ("Trans_Parameters", ":"),
        ("Pad2Length", "_-Pad2", 'self["Pad2Length"]'),
        ("Pad2", ":"),
        ("Trans_DataLength", "_-Trans_Data", 'self["Trans_DataLength"]'),
        ("Trans_Data", ":"),
    )


############# SMB_COM_TRANSACTION2 (0x32)


class SMBTransaction2_Parameters(SMBCommand_Parameters):
    structure = (
        ("TotalParameterCount", "<H"),
        ("TotalDataCount", "<H"),
        ("MaxParameterCount", "<H=1024"),
        ("MaxDataCount", "<H=65504"),
        ("MaxSetupCount", "<B=0"),
        ("Reserved1", "<B=0"),
        ("Flags", "<H=0"),
        ("Timeout", "<L=0"),
        ("Reserved2", "<H=0"),
        ("ParameterCount", "<H"),
        ("ParameterOffset", "<H"),
        ("DataCount", "<H"),
        ("DataOffset", "<H"),
        ("SetupCount", "<B=len(Setup)//2"),
        ("Reserved3", "<B=0"),
        ("SetupLength", "_-Setup", "SetupCount*2"),
        ("Setup", ":"),
    )


class SMBTransaction2Response_Parameters(SMBCommand_Parameters):
    structure = (
        ("TotalParameterCount", "<H"),
        ("TotalDataCount", "<H"),
        ("Reserved1", "<H=0"),
        ("ParameterCount", "<H"),
        ("ParameterOffset", "<H"),
        ("ParameterDisplacement", "<H=0"),
        ("DataCount", "<H"),
        ("DataOffset", "<H"),
        ("DataDisplacement", "<H=0"),
        ("SetupCount", "<B=0"),
        ("Reserved2", "<B=0"),
        ("SetupLength", "_-Setup", "SetupCount*2"),
        ("Setup", ":"),
    )


class SMBTransaction2_Data(Structure):
    structure = (
        #        ('NameLength','_-Name','1'),
        #        ('Name',':'),
        ("Pad1Length", "_-Pad1", 'self["Pad1Length"]'),
        ("Pad1", ":"),
        ("Trans_ParametersLength", "_-Trans_Parameters", 'self["Trans_ParametersLength"]'),
        ("Trans_Parameters", ":"),
        ("Pad2Length", "_-Pad2", 'self["Pad2Length"]'),
        ("Pad2", ":"),
        ("Trans_DataLength", "_-Trans_Data", 'self["Trans_DataLength"]'),
        ("Trans_Data", ":"),
    )


class SMBTransaction2Response_Data(Structure):
    structure = (
        ("Pad1Length", "_-Pad1", 'self["Pad1Length"]'),
        ("Pad1", ":"),
        ("Trans_ParametersLength", "_-Trans_Parameters", 'self["Trans_ParametersLength"]'),
        ("Trans_Parameters", ":"),
        ("Pad2Length", "_-Pad2", 'self["Pad2Length"]'),
        ("Pad2", ":"),
        ("Trans_DataLength", "_-Trans_Data", 'self["Trans_DataLength"]'),
        ("Trans_Data", ":"),
    )


############# SMB_COM_QUERY_INFORMATION (0x08)


class SMBQueryInformation_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("BufferFormat", "B=4"),
        ("FileName", "z"),
    )
    UnicodeStructure = (
        ("BufferFormat", "B=4"),
        ("FileName", "u"),
    )


class SMBQueryInformationResponse_Parameters(Structure):
    structure = (
        ("FileAttributes", "<H"),
        ("LastWriteTime", "<L"),
        ("FileSize", "<L"),
        ("Reserved", '"0123456789'),
    )


############# SMB_COM_TRANSACTION (0x25)
class SMBTransaction_Parameters(SMBCommand_Parameters):
    structure = (
        ("TotalParameterCount", "<H"),
        ("TotalDataCount", "<H"),
        ("MaxParameterCount", "<H=1024"),
        ("MaxDataCount", "<H=65504"),
        ("MaxSetupCount", "<B=0"),
        ("Reserved1", "<B=0"),
        ("Flags", "<H=0"),
        ("Timeout", "<L=0"),
        ("Reserved2", "<H=0"),
        ("ParameterCount", "<H"),
        ("ParameterOffset", "<H"),
        ("DataCount", "<H"),
        ("DataOffset", "<H"),
        ("SetupCount", "<B=len(Setup)//2"),
        ("Reserved3", "<B=0"),
        ("SetupLength", "_-Setup", "SetupCount*2"),
        ("Setup", ":"),
    )


class SMBTransactionResponse_Parameters(SMBCommand_Parameters):
    structure = (
        ("TotalParameterCount", "<H"),
        ("TotalDataCount", "<H"),
        ("Reserved1", "<H=0"),
        ("ParameterCount", "<H"),
        ("ParameterOffset", "<H"),
        ("ParameterDisplacement", "<H=0"),
        ("DataCount", "<H"),
        ("DataOffset", "<H"),
        ("DataDisplacement", "<H=0"),
        ("SetupCount", "<B"),
        ("Reserved2", "<B=0"),
        ("SetupLength", "_-Setup", "SetupCount*2"),
        ("Setup", ":"),
    )


# TODO: We should merge these both. But this will require fixing
# the instances where this structure is used on the client side
class SMBTransaction_SData(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("Name", "z"),
        ("Trans_ParametersLength", "_-Trans_Parameters"),
        ("Trans_Parameters", ":"),
        ("Trans_DataLength", "_-Trans_Data"),
        ("Trans_Data", ":"),
    )
    UnicodeStructure = (
        ("Pad", "B"),
        ("Name", "u"),
        ("Trans_ParametersLength", "_-Trans_Parameters"),
        ("Trans_Parameters", ":"),
        ("Trans_DataLength", "_-Trans_Data"),
        ("Trans_Data", ":"),
    )


class SMBTransaction_Data(Structure):
    structure = (
        ("NameLength", "_-Name"),
        ("Name", ":"),
        ("Trans_ParametersLength", "_-Trans_Parameters"),
        ("Trans_Parameters", ":"),
        ("Trans_DataLength", "_-Trans_Data"),
        ("Trans_Data", ":"),
    )


class SMBTransactionResponse_Data(Structure):
    structure = (
        ("Trans_ParametersLength", "_-Trans_Parameters"),
        ("Trans_Parameters", ":"),
        ("Trans_DataLength", "_-Trans_Data"),
        ("Trans_Data", ":"),
    )


############# SMB_COM_READ_ANDX (0x2E)
class SMBReadAndX_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("Fid", "<H"),
        ("Offset", "<L"),
        ("MaxCount", "<H"),
        ("MinCount", "<H=MaxCount"),
        ("_reserved", "<L=0x0"),
        ("Remaining", "<H=MaxCount"),
        ("HighOffset", "<L=0"),
    )


class SMBReadAndX_Parameters2(SMBAndXCommand_Parameters):
    structure = (
        ("Fid", "<H"),
        ("Offset", "<L"),
        ("MaxCount", "<H"),
        ("MinCount", "<H=MaxCount"),
        ("_reserved", "<L=0xffffffff"),
        ("Remaining", "<H=MaxCount"),
    )


class SMBReadAndXResponse_Parameters(SMBAndXCommand_Parameters):
    structure = (
        ("Remaining", "<H=0"),
        ("DataMode", "<H=0"),
        ("_reserved", "<H=0"),
        ("DataCount", "<H"),
        ("DataOffset", "<H"),
        ("DataCount_Hi", "<L"),
        ("_reserved2", '6s=""'),
    )


############# SMB_COM_ECHO (0x2B)
class SMBEcho_Data(Structure):
    structure = (("Data", ":"),)


class SMBEcho_Parameters(Structure):
    structure = (("EchoCount", "<H"),)


class SMBEchoResponse_Data(Structure):
    structure = (("Data", ":"),)


class SMBEchoResponse_Parameters(Structure):
    structure = (("SequenceNumber", "<H=1"),)


############# SMB_COM_QUERY_INFORMATION_DISK (0x80)
class SMBQueryInformationDiskResponse_Parameters(Structure):
    structure = (
        ("TotalUnits", "<H"),
        ("BlocksPerUnit", "<H"),
        ("BlockSize", "<H"),
        ("FreeUnits", "<H"),
        ("Reserved", "<H=0"),
    )


############# SMB_COM_LOGOFF_ANDX (0x74)
class SMBLogOffAndX(SMBAndXCommand_Parameters):
    strucure = ()


############# SMB_COM_CLOSE (0x04)
class SMBClose_Parameters(SMBCommand_Parameters):
    structure = (
        ("FID", "<H"),
        ("Time", "<L=0"),
    )


############# SMB_COM_FLUSH (0x05)
class SMBFlush_Parameters(SMBCommand_Parameters):
    structure = (("FID", "<H"),)


############# SMB_COM_CREATE_DIRECTORY (0x00)
class SMBCreateDirectory_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("BufferFormat", "<B=4"),
        ("DirectoryName", "z"),
    )
    UnicodeStructure = (
        ("BufferFormat", "<B=4"),
        ("DirectoryName", "u"),
    )


############# SMB_COM_DELETE (0x06)
class SMBDelete_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("BufferFormat", "<B=4"),
        ("FileName", "z"),
    )
    UnicodeStructure = (
        ("BufferFormat", "<B=4"),
        ("FileName", "u"),
    )


class SMBDelete_Parameters(Structure):
    structure = (("SearchAttributes", "<H"),)


############# SMB_COM_DELETE_DIRECTORY (0x01)
class SMBDeleteDirectory_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("BufferFormat", "<B=4"),
        ("DirectoryName", "z"),
    )
    UnicodeStructure = (
        ("BufferFormat", "<B=4"),
        ("DirectoryName", "u"),
    )


############# SMB_COM_CHECK_DIRECTORY (0x10)
class SMBCheckDirectory_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("BufferFormat", "<B=4"),
        ("DirectoryName", "z"),
    )
    UnicodeStructure = (
        ("BufferFormat", "<B=4"),
        ("DirectoryName", "u"),
    )


############# SMB_COM_RENAME (0x07)
class SMBRename_Parameters(SMBCommand_Parameters):
    structure = (("SearchAttributes", "<H"),)


class SMBRename_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("BufferFormat1", "<B=4"),
        ("OldFileName", "z"),
        ("BufferFormat2", "<B=4"),
        ("NewFileName", "z"),
    )
    UnicodeStructure = (
        ("BufferFormat1", "<B=4"),
        ("OldFileName", "u"),
        ("BufferFormat2", "<B=4"),
        ("Pad", "B=0"),
        ("NewFileName", "u"),
    )


############# SMB_COM_OPEN (0x02)
class SMBOpen_Parameters(SMBCommand_Parameters):
    structure = (
        ("DesiredAccess", "<H=0"),
        ("SearchAttributes", "<H=0"),
    )


class SMBOpen_Data(AsciiOrUnicodeStructure):
    AsciiStructure = (
        ("FileNameFormat", '"\x04'),
        ("FileName", "z"),
    )
    UnicodeStructure = (
        ("FileNameFormat", '"\x04'),
        ("FileName", "u"),
    )


class SMBOpenResponse_Parameters(SMBCommand_Parameters):
    structure = (
        ("Fid", "<H=0"),
        ("FileAttributes", "<H=0"),
        ("LastWriten", "<L=0"),
        ("FileSize", "<L=0"),
        ("GrantedAccess", "<H=0"),
    )


############# EXTENDED SECURITY CLASSES
class SMBExtended_Security_Parameters(Structure):
    structure = (
        ("DialectIndex", "<H"),
        ("SecurityMode", "<B"),
        ("MaxMpxCount", "<H"),
        ("MaxNumberVcs", "<H"),
        ("MaxBufferSize", "<L"),
        ("MaxRawSize", "<L"),
        ("SessionKey", "<L"),
        ("Capabilities", "<L"),
        ("LowDateTime", "<L"),
        ("HighDateTime", "<L"),
        ("ServerTimeZone", "<H"),
        ("ChallengeLength", "<B"),
    )


class SMBExtended_Security_Data(Structure):
    structure = (
        ("ServerGUID", "16s"),
        ("SecurityBlob", ":"),
    )


class SMBNTLMDialect_Parameters(Structure):
    structure = (
        ("DialectIndex", "<H"),
        ("SecurityMode", "<B"),
        ("MaxMpxCount", "<H"),
        ("MaxNumberVcs", "<H"),
        ("MaxBufferSize", "<L"),
        ("MaxRawSize", "<L"),
        ("SessionKey", "<L"),
        ("Capabilities", "<L"),
        ("LowDateTime", "<L"),
        ("HighDateTime", "<L"),
        ("ServerTimeZone", "<H"),
        ("ChallengeLength", "<B"),
    )


class SMBNTLMDialect_Data(Structure):
    structure = (
        ("ChallengeLength", "_-Challenge", 'self["ChallengeLength"]'),
        ("Challenge", ":"),
        ("Payload", ":"),
        # For some reason on an old Linux this field is not present, we have to check this out. There must be a flag stating this.
        ("DomainName", "_"),
        ("ServerName", "_"),
    )

    def __init__(self, data=None, alignment=0):
        Structure.__init__(self, data, alignment)
        # self['ChallengeLength']=8

    def fromString(self, data):
        Structure.fromString(self, data)
        self["DomainName"] = ""
        self["ServerName"] = ""


class SMB(object):
    class HostnameValidationException(Exception):
        pass

    # SMB Command Codes
    SMB_COM_CREATE_DIRECTORY = 0x00
    SMB_COM_DELETE_DIRECTORY = 0x01
    SMB_COM_OPEN = 0x02
    SMB_COM_CREATE = 0x03
    SMB_COM_CLOSE = 0x04
    SMB_COM_FLUSH = 0x05
    SMB_COM_DELETE = 0x06
    SMB_COM_RENAME = 0x07
    SMB_COM_QUERY_INFORMATION = 0x08
    SMB_COM_SET_INFORMATION = 0x09
    SMB_COM_READ = 0x0A
    SMB_COM_WRITE = 0x0B
    SMB_COM_LOCK_BYTE_RANGE = 0x0C
    SMB_COM_UNLOCK_BYTE_RANGE = 0x0D
    SMB_COM_CREATE_TEMPORARY = 0x0E
    SMB_COM_CREATE_NEW = 0x0F
    SMB_COM_CHECK_DIRECTORY = 0x10
    SMB_COM_PROCESS_EXIT = 0x11
    SMB_COM_SEEK = 0x12
    SMB_COM_LOCK_AND_READ = 0x13
    SMB_COM_WRITE_AND_UNLOCK = 0x14
    SMB_COM_READ_RAW = 0x1A
    SMB_COM_READ_MPX = 0x1B
    SMB_COM_READ_MPX_SECONDARY = 0x1C
    SMB_COM_WRITE_RAW = 0x1D
    SMB_COM_WRITE_MPX = 0x1E
    SMB_COM_WRITE_MPX_SECONDARY = 0x1F
    SMB_COM_WRITE_COMPLETE = 0x20
    SMB_COM_QUERY_SERVER = 0x21
    SMB_COM_SET_INFORMATION2 = 0x22
    SMB_COM_QUERY_INFORMATION2 = 0x23
    SMB_COM_LOCKING_ANDX = 0x24
    SMB_COM_TRANSACTION = 0x25
    SMB_COM_TRANSACTION_SECONDARY = 0x26
    SMB_COM_IOCTL = 0x27
    SMB_COM_IOCTL_SECONDARY = 0x28
    SMB_COM_COPY = 0x29
    SMB_COM_MOVE = 0x2A
    SMB_COM_ECHO = 0x2B
    SMB_COM_WRITE_AND_CLOSE = 0x2C
    SMB_COM_OPEN_ANDX = 0x2D
    SMB_COM_READ_ANDX = 0x2E
    SMB_COM_WRITE_ANDX = 0x2F
    SMB_COM_NEW_FILE_SIZE = 0x30
    SMB_COM_CLOSE_AND_TREE_DISC = 0x31
    SMB_COM_TRANSACTION2 = 0x32
    SMB_COM_TRANSACTION2_SECONDARY = 0x33
    SMB_COM_FIND_CLOSE2 = 0x34
    SMB_COM_FIND_NOTIFY_CLOSE = 0x35
    # Used by Xenix/Unix 0x60 - 0x6E
    SMB_COM_TREE_CONNECT = 0x70
    SMB_COM_TREE_DISCONNECT = 0x71
    SMB_COM_NEGOTIATE = 0x72
    SMB_COM_SESSION_SETUP_ANDX = 0x73
    SMB_COM_LOGOFF_ANDX = 0x74
    SMB_COM_TREE_CONNECT_ANDX = 0x75
    SMB_COM_QUERY_INFORMATION_DISK = 0x80
    SMB_COM_SEARCH = 0x81
    SMB_COM_FIND = 0x82
    SMB_COM_FIND_UNIQUE = 0x83
    SMB_COM_FIND_CLOSE = 0x84
    SMB_COM_NT_TRANSACT = 0xA0
    SMB_COM_NT_TRANSACT_SECONDARY = 0xA1
    SMB_COM_NT_CREATE_ANDX = 0xA2
    SMB_COM_NT_CANCEL = 0xA4
    SMB_COM_NT_RENAME = 0xA5
    SMB_COM_OPEN_PRINT_FILE = 0xC0
    SMB_COM_WRITE_PRINT_FILE = 0xC1
    SMB_COM_CLOSE_PRINT_FILE = 0xC2
    SMB_COM_GET_PRINT_QUEUE = 0xC3
    SMB_COM_READ_BULK = 0xD8
    SMB_COM_WRITE_BULK = 0xD9
    SMB_COM_WRITE_BULK_DATA = 0xDA

    # TRANSACT codes
    TRANS_TRANSACT_NMPIPE = 0x26

    # TRANSACT2 codes
    TRANS2_FIND_FIRST2 = 0x0001
    TRANS2_FIND_NEXT2 = 0x0002
    TRANS2_QUERY_FS_INFORMATION = 0x0003
    TRANS2_QUERY_PATH_INFORMATION = 0x0005
    TRANS2_QUERY_FILE_INFORMATION = 0x0007
    TRANS2_SET_FILE_INFORMATION = 0x0008
    TRANS2_SET_PATH_INFORMATION = 0x0006

    # Security Share Mode (Used internally by SMB class)
    SECURITY_SHARE_MASK = 0x01
    SECURITY_SHARE_SHARE = 0x00
    SECURITY_SHARE_USER = 0x01
    SECURITY_SIGNATURES_ENABLED = 0x04
    SECURITY_SIGNATURES_REQUIRED = 0x08

    # Security Auth Mode (Used internally by SMB class)
    SECURITY_AUTH_MASK = 0x02
    SECURITY_AUTH_ENCRYPTED = 0x02
    SECURITY_AUTH_PLAINTEXT = 0x00

    # Raw Mode Mask (Used internally by SMB class. Good for dialect up to and including LANMAN2.1)
    RAW_READ_MASK = 0x01
    RAW_WRITE_MASK = 0x02

    # Capabilities Mask (Used internally by SMB class. Good for dialect NT LM 0.12)
    CAP_RAW_MODE = 0x00000001
    CAP_MPX_MODE = 0x0002
    CAP_UNICODE = 0x0004
    CAP_LARGE_FILES = 0x0008
    CAP_EXTENDED_SECURITY = 0x80000000
    CAP_USE_NT_ERRORS = 0x40
    CAP_NT_SMBS = 0x10
    CAP_LARGE_READX = 0x00004000
    CAP_LARGE_WRITEX = 0x00008000
    CAP_RPC_REMOTE_APIS = 0x20

    # Flags1 Mask
    FLAGS1_LOCK_AND_READ_OK = 0x01
    FLAGS1_PATHCASELESS = 0x08
    FLAGS1_CANONICALIZED_PATHS = 0x10
    FLAGS1_REPLY = 0x80

    # Flags2 Mask
    FLAGS2_LONG_NAMES = 0x0001
    FLAGS2_EAS = 0x0002
    FLAGS2_SMB_SECURITY_SIGNATURE = 0x0004
    FLAGS2_IS_LONG_NAME = 0x0040
    FLAGS2_DFS = 0x1000
    FLAGS2_PAGING_IO = 0x2000
    FLAGS2_NT_STATUS = 0x4000
    FLAGS2_UNICODE = 0x8000
    FLAGS2_COMPRESSED = 0x0008
    FLAGS2_SMB_SECURITY_SIGNATURE_REQUIRED = 0x0010
    FLAGS2_EXTENDED_SECURITY = 0x0800

    # Dialect's Security Mode flags
    NEGOTIATE_USER_SECURITY = 0x01
    NEGOTIATE_ENCRYPT_PASSWORDS = 0x02
    NEGOTIATE_SECURITY_SIGNATURE_ENABLE = 0x04
    NEGOTIATE_SECURITY_SIGNATURE_REQUIRED = 0x08

    # Tree Connect AndX Response optionalSuppor flags
    SMB_SUPPORT_SEARCH_BITS = 0x01
    SMB_SHARE_IS_IN_DFS = 0x02

    def __init__(
        self,
        remote_name,
        remote_host,
        my_name=None,
        host_type=nmb.TYPE_SERVER,
        sess_port=445,
        timeout=None,
        UDP=0,
        session=None,
        negPacket=None,
    ):
        # The uid attribute will be set when the client calls the login() method
        self._uid = 0
        self.__server_name = ""
        self.__client_name = ""
        self.__server_os = ""
        self.__server_os_major = None
        self.__server_os_minor = None
        self.__server_os_build = None
        self.__server_lanman = ""
        self.__server_domain = ""
        self.__server_dns_domain_name = ""
        self.__server_dns_host_name = ""
        self.__remote_name = remote_name.upper()
        self.__remote_host = remote_host
        self.__isNTLMv2 = True
        self._dialects_parameters = None
        self._dialects_data = None
        self._doKerberos = False

        # Credentials
        self.__userName = b""
        self.__password = b""
        self.__domain = b""
        self.__lmhash = b""
        self.__nthash = b""
        self.__aesKey = b""
        self.__kdc = b""
        self.__TGT = None
        self.__TGS = None

        # Negotiate Protocol Result, used everywhere
        # Could be extended or not, flags should be checked before
        self._dialect_data = 0
        self._dialect_parameters = 0
        self._action = 0
        self._sess = None
        self.encrypt_passwords = True
        self.tid = 0
        self.fid = 0

        # Strict host validation - off by default
        self._strict_hostname_validation = False
        self._validation_allow_absent = True
        self._accepted_hostname = ""

        # Signing stuff
        self._SignSequenceNumber = 0
        self._SigningSessionKey = b""
        self._SigningChallengeResponse = b""
        self._SignatureEnabled = False
        self._SignatureVerificationEnabled = False
        self._SignatureRequired = False

        # Base flags (default flags, can be overridden using set_flags())
        self.__flags1 = SMB.FLAGS1_PATHCASELESS | SMB.FLAGS1_CANONICALIZED_PATHS
        self.__flags2 = SMB.FLAGS2_EXTENDED_SECURITY | SMB.FLAGS2_NT_STATUS | SMB.FLAGS2_LONG_NAMES

        if timeout is None:
            self.__timeout = 60
        else:
            self.__timeout = timeout

        # If port 445 and the name sent is *SMBSERVER we're setting the name to the IP.
        # This is to help some old applications still believing
        # *SMSBSERVER will work against modern OSes. If port is NETBIOS_SESSION_PORT the user better
        # know about *SMBSERVER's limitations
        if sess_port == 445 and remote_name == "*SMBSERVER":
            self.__remote_name = remote_host

        # This is on purpose. I'm still not convinced to do a socket.gethostname() if not specified
        if my_name is None:
            self.__client_name = b""
        else:
            self.__client_name = my_name

        if session is None:
            if not my_name:
                # If destination port is 139 yes, there's some client disclosure
                my_name = socket.gethostname()
                i = my_name.find(".")
                if i > -1:
                    my_name = my_name[:i]

            if UDP:
                self._sess = nmb.NetBIOSUDPSession(
                    my_name, remote_name, remote_host, host_type, sess_port, self.__timeout
                )
            else:
                self._sess = nmb.NetBIOSTCPSession(
                    my_name, remote_name, remote_host, host_type, sess_port, self.__timeout
                )

                # Initialize session values (_dialect_data and _dialect_parameters)
                self.neg_session()

                # Call login() without any authentication information to
                # setup a session if the remote server
                # is in share mode.
                if (self._dialects_parameters["SecurityMode"] & SMB.SECURITY_SHARE_MASK) == SMB.SECURITY_SHARE_SHARE:
                    self.login("", "")
        else:
            self._sess = session
            self.neg_session(negPacket=negPacket)
            # Call login() without any authentication information to
            # setup a session if the remote server
            # is in share mode.
            if (self._dialects_parameters["SecurityMode"] & SMB.SECURITY_SHARE_MASK) == SMB.SECURITY_SHARE_SHARE:
                self.login("", "")

    @staticmethod
    def ntlm_supported():
        return False

    def getKerberos(self):
        return self._doKerberos

    def get_remote_name(self):
        return self.__remote_name

    def set_remote_name(self, name):
        self.__remote_name = name
        return True

    def set_hostname_validation(self, validate, accept_empty, hostname):
        self._strict_hostname_validation = validate
        self._validation_allow_absent = accept_empty
        self._accepted_hostname = hostname

    def get_remote_host(self):
        return self.__remote_host

    def get_flags(self):
        return self.__flags1, self.__flags2

    def set_flags(self, flags1=None, flags2=None):
        if flags1 is not None:
            self.__flags1 = flags1
        if flags2 is not None:
            self.__flags2 = flags2

    def set_timeout(self, timeout):
        prev_timeout = self.__timeout
        self.__timeout = timeout
        return prev_timeout

    def get_timeout(self):
        return self.__timeout

    @contextmanager
    def use_timeout(self, timeout):
        prev_timeout = self.set_timeout(timeout)
        try:
            yield
        finally:
            self.set_timeout(prev_timeout)

    def get_session(self):
        return self._sess

    def get_tid(self):
        return self.tid

    def get_fid(self):
        return self.fid

    def isGuestSession(self):
        return self._action & SMB_SETUP_GUEST

    def doesSupportNTLMv2(self):
        return self.__isNTLMv2

    def close_session(self):
        if self._sess:
            self._sess.close()
            self._sess = None

    def recvSMB(self):
        r = self._sess.recv_packet(self.__timeout)
        return NewSMBPacket(data=r.get_trailer())

    @staticmethod
    def __decode_trans(params, data):
        totparamcnt, totdatacnt, _, paramcnt, paramoffset, paramds, datacnt, dataoffset, datads, setupcnt = unpack(
            "<HHHHHHHHHB", params[:19]
        )
        if paramcnt + paramds < totparamcnt or datacnt + datads < totdatacnt:
            has_more = 1
        else:
            has_more = 0
        paramoffset = paramoffset - 55 - setupcnt * 2
        dataoffset = dataoffset - 55 - setupcnt * 2
        return (
            has_more,
            params[20 : 20 + setupcnt * 2],
            data[paramoffset : paramoffset + paramcnt],
            data[dataoffset : dataoffset + datacnt],
        )

    # TODO: Move this to NewSMBPacket, it belongs there
    def signSMB(self, packet, signingSessionKey, signingChallengeResponse):
        # This logic MUST be applied for messages sent in response to any of the higher-layer actions and in
        # compliance with the message sequencing rules.
        #  * The client or server that sends the message MUST provide the 32-bit sequence number for this
        #    message, as specified in sections 3.2.4.1 and 3.3.4.1.
        #  * The SMB_FLAGS2_SMB_SECURITY_SIGNATURE flag in the header MUST be set.
        #  * To generate the signature, a 32-bit sequence number is copied into the
        #    least significant 32 bits of the SecuritySignature field and the remaining
        #    4 bytes are set to 0x00.
        #  * The MD5 algorithm, as specified in [RFC1321], MUST be used to generate a hash of the SMB
        #    message from the start of the SMB Header, which is defined as follows.
        #    CALL MD5Init( md5context )
        #    CALL MD5Update( md5context, Connection.SigningSessionKey )
        #    CALL MD5Update( md5context, Connection.SigningChallengeResponse )
        #    CALL MD5Update( md5context, SMB message )
        #    CALL MD5Final( digest, md5context )
        #    SET signature TO the first 8 bytes of the digest
        # The resulting 8-byte signature MUST be copied into the SecuritySignature field of the SMB Header,
        # after which the message can be transmitted.

        # print "seq(%d) signingSessionKey %r, signingChallengeResponse %r" % (self._SignSequenceNumber, signingSessionKey, signingChallengeResponse)
        packet["SecurityFeatures"] = pack("<q", self._SignSequenceNumber)
        # Sign with the sequence
        m = hashlib.md5()
        m.update(signingSessionKey)
        m.update(signingChallengeResponse)
        m.update(packet.getData())
        # Replace sequence with acual hash
        packet["SecurityFeatures"] = m.digest()[:8]
        if self._SignatureVerificationEnabled:
            self._SignSequenceNumber += 1
        else:
            self._SignSequenceNumber += 2

    def checkSignSMB(self, packet, signingSessionKey, signingChallengeResponse):
        # Let's check
        signature = packet["SecurityFeatures"]
        # print "Signature received: %r " % signature
        self.signSMB(packet, signingSessionKey, signingChallengeResponse)
        # print "Signature calculated: %r" % packet['SecurityFeatures']
        if self._SignatureVerificationEnabled is not True:
            self._SignSequenceNumber -= 1
        return packet["SecurityFeatures"] == signature

    def sendSMB(self, smb):
        smb["Uid"] = self._uid
        # At least on AIX, PIDs can exceed 16 bits, so we mask them out
        smb["Pid"] = os.getpid() & 0xFFFF
        # set flags
        smb["Flags1"] |= self.__flags1
        smb["Flags2"] |= self.__flags2
        if self._SignatureEnabled:
            smb["Flags2"] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE
            self.signSMB(smb, self._SigningSessionKey, self._SigningChallengeResponse)

        self._sess.send_packet(smb.getData())

    @staticmethod
    def isValidAnswer(s, cmd):
        while 1:
            if s.rawData():
                if s.get_command() == cmd:
                    if s.get_error_class() == 0x00 and s.get_error_code() == 0x00:
                        return 1
                    else:
                        raise SessionError(
                            "SMB Library Error",
                            s.get_error_class() + (s.get_reserved() << 8),
                            s.get_error_code(),
                            s.get_flags2() & SMB.FLAGS2_NT_STATUS,
                        )
                else:
                    break
        return 0

    def neg_session(self, extended_security=True, negPacket=None):
        def parsePacket(smb):
            # If server speaks Unicode, let's set that flag from now on
            if smb["Flags2"] & SMB.FLAGS2_UNICODE:
                self.__flags2 |= SMB.FLAGS2_UNICODE

            if smb.isValidAnswer(SMB.SMB_COM_NEGOTIATE):
                sessionResponse = SMBCommand(smb["Data"][0])
                self._dialects_parameters = SMBNTLMDialect_Parameters(sessionResponse["Parameters"])
                self._dialects_data = SMBNTLMDialect_Data()
                self._dialects_data["ChallengeLength"] = self._dialects_parameters["ChallengeLength"]
                self._dialects_data.fromString(sessionResponse["Data"])
                if self._dialects_parameters["Capabilities"] & SMB.CAP_EXTENDED_SECURITY:
                    # Whether we choose it or it is enforced by the server, we go for extended security
                    self._dialects_parameters = SMBExtended_Security_Parameters(sessionResponse["Parameters"])
                    self._dialects_data = SMBExtended_Security_Data(sessionResponse["Data"])
                    # Let's setup some variable for later use
                    if self._dialects_parameters["SecurityMode"] & SMB.SECURITY_SIGNATURES_REQUIRED:
                        self._SignatureRequired = True

                    # Interestingly, the security Blob might be missing sometimes.
                    # spnego = SPNEGO_NegTokenInit(self._dialects_data['SecurityBlob'])
                    # for i in spnego['MechTypes']:
                    #      print "Mech Found: %s" % MechTypes[i]
                    return 1

                # If not, let's try the old way
                else:
                    if self._dialects_data["ServerName"] is not None:
                        self.__server_name = self._dialects_data["ServerName"]

                    if self._dialects_parameters["DialectIndex"] == 0xFFFF:
                        raise UnsupportedFeature("Remote server does not know NT LM 0.12")
                    return 1
            else:
                return 0

        if negPacket is None:
            smb = NewSMBPacket()
            negSession = SMBCommand(SMB.SMB_COM_NEGOTIATE)
            flags2 = self.get_flags()[1]
            if extended_security is True:
                self.set_flags(flags2=flags2 | SMB.FLAGS2_EXTENDED_SECURITY)
            else:
                self.set_flags(flags2=flags2 & (~SMB.FLAGS2_EXTENDED_SECURITY))

            negSession["Data"] = b"\x02NT LM 0.12\x00"
            smb.addCommand(negSession)
            self.sendSMB(smb)

            while 1:
                smb = self.recvSMB()
                return parsePacket(smb)
        else:

            return parsePacket(NewSMBPacket(data=negPacket))

    def tree_connect(self, path, password="", service=SERVICE_ANY):
        LOG.warning(
            "[MS-CIFS] This is an original Core Protocol command.This command has been deprecated.Client Implementations SHOULD use SMB_COM_TREE_CONNECT_ANDX"
        )

        # return 0x800
        if password:
            # Password is only encrypted if the server passed us an "encryption" during protocol dialect
            if self._dialects_parameters["ChallengeLength"] > 0:
                # this code is untested
                password = self.get_ntlmv1_response(ntlm.compute_lmhash(password))

        if not unicode_support:
            if unicode_convert:
                path = str(path)
            else:
                raise Exception("SMB: Can\t conver path from unicode!")

        smb = NewSMBPacket()
        treeConnect = SMBCommand(SMB.SMB_COM_TREE_CONNECT)
        treeConnect["Parameters"] = SMBTreeConnect_Parameters()
        treeConnect["Data"] = SMBTreeConnect_Data()
        treeConnect["Data"]["Path"] = path.upper()
        treeConnect["Data"]["Password"] = password
        treeConnect["Data"]["Service"] = service
        smb.addCommand(treeConnect)
        self.sendSMB(smb)

        while 1:
            smb = self.recvSMB()
            if smb.isValidAnswer(SMB.SMB_COM_TREE_CONNECT):
                # XXX Here we are ignoring the rest of the response
                return smb["Tid"]
            return smb["Tid"]

    def get_uid(self):
        return self._uid

    def set_uid(self, uid):
        self._uid = uid

    def tree_connect_andx(self, path, password=None, service=SERVICE_ANY, smb_packet=None):
        if password:
            # Password is only encrypted if the server passed us an "encryption" during protocol dialect
            if self._dialects_parameters["ChallengeLength"] > 0:
                # this code is untested
                password = self.get_ntlmv1_response(ntlm.compute_lmhash(password))
        else:
            password = "\x00"

        if not unicode_support:
            if unicode_convert:
                path = str(path)
            else:
                raise Exception("SMB: Can\t convert path from unicode!")

        if smb_packet is None:
            smb = NewSMBPacket()
        else:
            smb = smb_packet

        # Just in case this came with the full path ,let's just leave
        # the sharename, we'll take care of the rest

        share = path.split("\\")[-1]
        try:
            _, _, _, _, sockaddr = socket.getaddrinfo(self.get_remote_host(), 80, 0, 0, socket.IPPROTO_TCP)[0]
            remote_host = sockaddr[0]
        except Exception:
            remote_host = self.get_remote_host()

        path = "\\\\" + remote_host + "\\" + share
        path = path.upper().encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else path

        treeConnect = SMBCommand(SMB.SMB_COM_TREE_CONNECT_ANDX)
        treeConnect["Parameters"] = SMBTreeConnectAndX_Parameters()
        treeConnect["Data"] = SMBTreeConnectAndX_Data(flags=self.__flags2)
        treeConnect["Parameters"]["PasswordLength"] = len(password)
        treeConnect["Data"]["Password"] = password
        treeConnect["Data"]["Path"] = path
        treeConnect["Data"]["Service"] = service

        if self.__flags2 & SMB.FLAGS2_UNICODE:
            treeConnect["Data"]["Pad"] = 0x0

        smb.addCommand(treeConnect)

        # filename = "\PIPE\epmapper"

        # ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX)
        # ntCreate['Parameters'] = SMBNtCreateAndX_Parameters()
        # ntCreate['Data']       = SMBNtCreateAndX_Data()
        # ntCreate['Parameters']['FileNameLength'] = len(filename)
        # ntCreate['Parameters']['CreateFlags'] = 0
        # ntCreate['Parameters']['AccessMask'] = 0x3
        # ntCreate['Parameters']['CreateOptions'] = 0x0
        # ntCreate['Data']['FileName'] = filename

        # smb.addCommand(ntCreate)
        self.sendSMB(smb)

        while 1:
            smb = self.recvSMB()
            if smb.isValidAnswer(SMB.SMB_COM_TREE_CONNECT_ANDX):
                # XXX Here we are ignoring the rest of the response
                self.tid = smb["Tid"]
                return self.tid
            self.tid = smb["Tid"]
            return self.tid

    # backwars compatibility
    connect_tree = tree_connect_andx

    @staticmethod
    def getDialect():
        return SMB_DIALECT

    def get_server_name(self):
        # return self._dialects_data['ServerName']
        return self.__server_name

    def get_client_name(self):
        return self.__client_name

    def get_session_key(self):
        return self._SigningSessionKey

    def set_session_key(self, key):
        self._SignatureEnabled = True
        self._SignSequenceNumber = 2
        self._SigningSessionKey = key

    def get_encryption_key(self):
        if "Challenge" in self._dialects_data.fields:
            return self._dialects_data["Challenge"]
        else:
            return None

    def get_server_time(self):
        timestamp = self._dialects_parameters["HighDateTime"]
        timestamp <<= 32
        timestamp |= self._dialects_parameters["LowDateTime"]
        timestamp -= 116444736000000000
        timestamp //= 10000000
        d = datetime.datetime.utcfromtimestamp(timestamp)
        return d.strftime("%a, %d %b %Y %H:%M:%S GMT")

    def disconnect_tree(self, tid):
        smb = NewSMBPacket()
        smb["Tid"] = tid

        smb.addCommand(SMBCommand(SMB.SMB_COM_TREE_DISCONNECT))

        self.sendSMB(smb)
        self.recvSMB()

    def open(self, tid, filename, open_mode, desired_access):
        filename = filename.replace("/", "\\")
        filename = filename.encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else filename

        smb = NewSMBPacket()
        smb["Tid"] = tid

        openFile = SMBCommand(SMB.SMB_COM_OPEN)
        openFile["Parameters"] = SMBOpen_Parameters()
        openFile["Parameters"]["DesiredAccess"] = desired_access
        openFile["Parameters"]["OpenMode"] = open_mode
        openFile["Parameters"]["SearchAttributes"] = ATTR_READONLY | ATTR_HIDDEN | ATTR_ARCHIVE
        openFile["Data"] = SMBOpen_Data(flags=self.__flags2)
        openFile["Data"]["FileName"] = filename

        smb.addCommand(openFile)

        self.sendSMB(smb)

        smb = self.recvSMB()
        if smb.isValidAnswer(SMB.SMB_COM_OPEN):
            # XXX Here we are ignoring the rest of the response
            openFileResponse = SMBCommand(smb["Data"][0])
            openFileParameters = SMBOpenResponse_Parameters(openFileResponse["Parameters"])

            return (
                openFileParameters["Fid"],
                openFileParameters["FileAttributes"],
                openFileParameters["LastWriten"],
                openFileParameters["FileSize"],
                openFileParameters["GrantedAccess"],
            )

    def open_andx(self, tid, filename, open_mode, desired_access):
        filename = filename.replace("/", "\\")
        filename = filename.encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else filename

        smb = NewSMBPacket()
        smb["Tid"] = tid

        openFile = SMBCommand(SMB.SMB_COM_OPEN_ANDX)
        openFile["Parameters"] = SMBOpenAndX_Parameters()
        openFile["Parameters"]["DesiredAccess"] = desired_access
        openFile["Parameters"]["OpenMode"] = open_mode
        openFile["Parameters"]["SearchAttributes"] = ATTR_READONLY | ATTR_HIDDEN | ATTR_ARCHIVE
        openFile["Data"] = SMBOpenAndX_Data(flags=self.__flags2)
        openFile["Data"]["FileName"] = filename

        if self.__flags2 & SMB.FLAGS2_UNICODE:
            openFile["Data"]["Pad"] = 0x0

        smb.addCommand(openFile)

        self.sendSMB(smb)

        smb = self.recvSMB()
        if smb.isValidAnswer(SMB.SMB_COM_OPEN_ANDX):
            # XXX Here we are ignoring the rest of the response
            openFileResponse = SMBCommand(smb["Data"][0])
            openFileParameters = SMBOpenAndXResponse_Parameters(openFileResponse["Parameters"])

            return (
                openFileParameters["Fid"],
                openFileParameters["FileAttributes"],
                openFileParameters["LastWriten"],
                openFileParameters["FileSize"],
                openFileParameters["GrantedAccess"],
                openFileParameters["FileType"],
                openFileParameters["IPCState"],
                openFileParameters["Action"],
                openFileParameters["ServerFid"],
            )

    def close(self, tid, fid):
        smb = NewSMBPacket()
        smb["Tid"] = tid

        closeFile = SMBCommand(SMB.SMB_COM_CLOSE)
        closeFile["Parameters"] = SMBClose_Parameters()
        closeFile["Parameters"]["FID"] = fid
        smb.addCommand(closeFile)

        self.sendSMB(smb)
        smb = self.recvSMB()
        if smb.isValidAnswer(SMB.SMB_COM_CLOSE):
            return 1
        return 0

    def send_trans(self, tid, setup, name, param, data, noAnswer=0):
        smb = NewSMBPacket()
        smb["Tid"] = tid

        transCommand = SMBCommand(SMB.SMB_COM_TRANSACTION)
        transCommand["Parameters"] = SMBTransaction_Parameters()
        transCommand["Data"] = SMBTransaction_Data()

        transCommand["Parameters"]["Setup"] = setup
        transCommand["Parameters"]["TotalParameterCount"] = len(param)
        transCommand["Parameters"]["TotalDataCount"] = len(data)

        transCommand["Parameters"]["ParameterCount"] = len(param)
        transCommand["Parameters"]["ParameterOffset"] = 32 + 3 + 28 + len(setup) + len(name)

        transCommand["Parameters"]["DataCount"] = len(data)
        transCommand["Parameters"]["DataOffset"] = transCommand["Parameters"]["ParameterOffset"] + len(param)

        transCommand["Data"]["Name"] = name
        transCommand["Data"]["Trans_Parameters"] = param
        transCommand["Data"]["Trans_Data"] = data

        if noAnswer:
            transCommand["Parameters"]["Flags"] = TRANS_NO_RESPONSE

        smb.addCommand(transCommand)

        self.sendSMB(smb)

    def send_trans2(self, tid, setup, name, param, data):
        smb = NewSMBPacket()
        smb["Tid"] = tid

        command = pack("<H", setup)

        transCommand = SMBCommand(SMB.SMB_COM_TRANSACTION2)
        transCommand["Parameters"] = SMBTransaction2_Parameters()
        transCommand["Parameters"]["MaxDataCount"] = self._dialects_parameters["MaxBufferSize"]
        transCommand["Data"] = SMBTransaction2_Data()

        transCommand["Parameters"]["Setup"] = command
        transCommand["Parameters"]["TotalParameterCount"] = len(param)
        transCommand["Parameters"]["TotalDataCount"] = len(data)

        if len(param) > 0:
            padLen = (4 - (32 + 2 + 28 + len(command)) % 4) % 4
            padBytes = "\xFF" * padLen
            transCommand["Data"]["Pad1"] = padBytes
        else:
            transCommand["Data"]["Pad1"] = ""
            padLen = 0

        transCommand["Parameters"]["ParameterCount"] = len(param)
        transCommand["Parameters"]["ParameterOffset"] = 32 + 2 + 28 + len(command) + len(name) + padLen

        if len(data) > 0:
            pad2Len = (4 - (32 + 2 + 28 + len(command) + padLen + len(param)) % 4) % 4
            transCommand["Data"]["Pad2"] = "\xFF" * pad2Len
        else:
            transCommand["Data"]["Pad2"] = ""
            pad2Len = 0

        transCommand["Parameters"]["DataCount"] = len(data)
        transCommand["Parameters"]["DataOffset"] = transCommand["Parameters"]["ParameterOffset"] + len(param) + pad2Len

        transCommand["Data"]["Name"] = name
        transCommand["Data"]["Trans_Parameters"] = param
        transCommand["Data"]["Trans_Data"] = data
        smb.addCommand(transCommand)

        self.sendSMB(smb)

    def query_file_info(self, tid, fid, fileInfoClass=SMB_QUERY_FILE_STANDARD_INFO):
        self.send_trans2(tid, SMB.TRANS2_QUERY_FILE_INFORMATION, "\x00", pack("<HH", fid, fileInfoClass), "")

        resp = self.recvSMB()
        if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2):
            trans2Response = SMBCommand(resp["Data"][0])
            trans2Parameters = SMBTransaction2Response_Parameters(trans2Response["Parameters"])
            # Remove Potential Prefix Padding
            return trans2Response["Data"][-trans2Parameters["TotalDataCount"] :]

    def __nonraw_retr_file(self, tid, fid, offset, datasize, callback):
        if (self._dialects_parameters["Capabilities"] & SMB.CAP_LARGE_READX) and self._SignatureEnabled is False:
            max_buf_size = 65000
        else:
            max_buf_size = self._dialects_parameters["MaxBufferSize"] & ~0x3FF  # Read in multiple KB blocks

        read_offset = offset
        while read_offset < datasize:
            data = self.read_andx(tid, fid, read_offset, max_buf_size)

            callback(data)
            read_offset += len(data)

    def __nonraw_stor_file(self, tid, fid, offset, datasize, callback):
        if (self._dialects_parameters["Capabilities"] & SMB.CAP_LARGE_WRITEX) and self._SignatureEnabled is False:
            max_buf_size = 65000
        else:
            max_buf_size = self._dialects_parameters["MaxBufferSize"] & ~0x3FF  # Write in multiple KB blocks

        write_offset = offset
        while 1:
            data = callback(max_buf_size)
            if not data:
                break

            smb = self.write_andx(tid, fid, data, write_offset)
            writeResponse = SMBCommand(smb["Data"][0])
            writeResponseParameters = SMBWriteAndXResponse_Parameters(writeResponse["Parameters"])
            write_offset += writeResponseParameters["Count"]

    def get_server_domain(self):
        return self.__server_domain

    def get_server_dns_domain_name(self):
        return self.__server_dns_domain_name

    def get_server_dns_host_name(self):
        return self.__server_dns_host_name

    def get_server_os(self):
        return self.__server_os

    def get_server_os_major(self):
        return self.__server_os_major

    def get_server_os_minor(self):
        return self.__server_os_minor

    def get_server_os_build(self):
        return self.__server_os_build

    def set_server_os(self, os):
        self.__server_os = os

    def get_server_lanman(self):
        return self.__server_lanman

    def is_login_required(self):
        # Login is required if share mode is user.
        # Otherwise only public services or services in share mode
        # are allowed.
        return (self._dialects_parameters["SecurityMode"] & SMB.SECURITY_SHARE_MASK) == SMB.SECURITY_SHARE_USER

    def is_signing_required(self):
        return self._SignatureRequired

    def get_ntlmv1_response(self, key):
        challenge = self._dialects_data["Challenge"]
        return ntlm.get_ntlmv1_response(key, challenge)

    def perform_hostname_validation(self):
        if self.__server_name == "":
            if not self._validation_allow_absent:
                raise self.HostnameValidationException(
                    "Hostname was not supplied by target host and absent validation is disallowed"
                )
            return
        if (
            self.__server_name.lower() != self._accepted_hostname.lower()
            and self.__server_dns_host_name.lower() != self._accepted_hostname.lower()
        ):
            raise self.HostnameValidationException(
                "Supplied hostname %s does not match reported hostnames %s or %s"
                % (self._accepted_hostname.lower(), self.__server_name.lower(), self.__server_dns_host_name.lower())
            )

    def kerberos_login(
        self, user, password, domain="", lmhash="", nthash="", aesKey="", kdcHost="", TGT=None, TGS=None
    ):
        # Importing down here so pyasn1 is not required if kerberos is not used.
        import datetime

        from pyasn1.codec.der import decoder, encoder

        from impacket.krb5 import constants
        from impacket.krb5.asn1 import AP_REQ, TGS_REP, Authenticator, seq_set
        from impacket.krb5.kerberosv5 import getKerberosTGS, getKerberosTGT
        from impacket.krb5.types import KerberosTime, Principal, Ticket

        # login feature does not support unicode
        # disable it if enabled
        flags2 = self.__flags2
        if flags2 & SMB.FLAGS2_UNICODE:
            self.__flags2 = flags2 & (flags2 ^ SMB.FLAGS2_UNICODE)

        # If TGT or TGS are specified, they are in the form of:
        # TGS['KDC_REP'] = the response from the server
        # TGS['cipher'] = the cipher used
        # TGS['sessionKey'] = the sessionKey
        # If we have hashes, normalize them
        if lmhash != "" or nthash != "":
            if len(lmhash) % 2:
                lmhash = "0%s" % lmhash
            if len(nthash) % 2:
                nthash = "0%s" % nthash
            try:  # just in case they were converted already
                lmhash = a2b_hex(lmhash)
                nthash = a2b_hex(nthash)
            except:
                pass

        self.__userName = user
        self.__password = password
        self.__domain = domain
        self.__lmhash = lmhash
        self.__nthash = nthash
        self.__aesKey = aesKey
        self.__kdc = kdcHost
        self.__TGT = TGT
        self.__TGS = TGS
        self._doKerberos = True

        # First of all, we need to get a TGT for the user
        userName = Principal(user, type=constants.PrincipalNameType.NT_PRINCIPAL.value)
        if TGT is None:
            if TGS is None:
                tgt, cipher, oldSessionKey, sessionKey = getKerberosTGT(
                    userName, password, domain, lmhash, nthash, aesKey, kdcHost
                )
        else:
            tgt = TGT["KDC_REP"]
            cipher = TGT["cipher"]
            sessionKey = TGT["sessionKey"]

        # Now that we have the TGT, we should ask for a TGS for cifs

        if TGS is None:
            serverName = Principal("cifs/%s" % self.__remote_name, type=constants.PrincipalNameType.NT_SRV_INST.value)
            tgs, cipher, oldSessionKey, sessionKey = getKerberosTGS(
                serverName, domain, kdcHost, tgt, cipher, sessionKey
            )
        else:
            tgs = TGS["KDC_REP"]
            cipher = TGS["cipher"]
            sessionKey = TGS["sessionKey"]

        smb = NewSMBPacket()

        # Are we required to sign SMB? If so we do it, if not we skip it
        if self._SignatureRequired:
            smb["Flags2"] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE

        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
        sessionSetup["Parameters"] = SMBSessionSetupAndX_Extended_Parameters()
        sessionSetup["Data"] = SMBSessionSetupAndX_Extended_Data()

        sessionSetup["Parameters"]["MaxBufferSize"] = 61440
        sessionSetup["Parameters"]["MaxMpxCount"] = 2
        sessionSetup["Parameters"]["VcNumber"] = 1
        sessionSetup["Parameters"]["SessionKey"] = 0
        sessionSetup["Parameters"]["Capabilities"] = (
            SMB.CAP_EXTENDED_SECURITY
            | SMB.CAP_USE_NT_ERRORS
            | SMB.CAP_UNICODE
            | SMB.CAP_LARGE_READX
            | SMB.CAP_LARGE_WRITEX
        )

        # Let's build a NegTokenInit with the NTLMSSP
        # TODO: In the future we should be able to choose different providers

        blob = SPNEGO_NegTokenInit()

        # Kerberos v5 mech
        blob["MechTypes"] = [TypesMech["MS KRB5 - Microsoft Kerberos 5"]]

        # Let's extract the ticket from the TGS
        tgs = decoder.decode(tgs, asn1Spec=TGS_REP())[0]
        ticket = Ticket()
        ticket.from_asn1(tgs["ticket"])

        # Now let's build the AP_REQ
        apReq = AP_REQ()
        apReq["pvno"] = 5
        apReq["msg-type"] = int(constants.ApplicationTagNumbers.AP_REQ.value)

        opts = list()
        apReq["ap-options"] = constants.encodeFlags(opts)
        seq_set(apReq, "ticket", ticket.to_asn1)

        authenticator = Authenticator()
        authenticator["authenticator-vno"] = 5
        authenticator["crealm"] = domain
        seq_set(authenticator, "cname", userName.components_to_asn1)
        now = datetime.datetime.utcnow()

        authenticator["cusec"] = now.microsecond
        authenticator["ctime"] = KerberosTime.to_asn1(now)

        encodedAuthenticator = encoder.encode(authenticator)

        # Key Usage 11
        # AP-REQ Authenticator (includes application authenticator
        # subkey), encrypted with the application session key
        # (Section 5.5.1)
        encryptedEncodedAuthenticator = cipher.encrypt(sessionKey, 11, encodedAuthenticator, None)

        apReq["authenticator"] = noValue
        apReq["authenticator"]["etype"] = cipher.enctype
        apReq["authenticator"]["cipher"] = encryptedEncodedAuthenticator

        blob["MechToken"] = pack("B", ASN1_AID) + asn1encode(
            pack("B", ASN1_OID) + asn1encode(TypesMech["KRB5 - Kerberos 5"]) + KRB5_AP_REQ + encoder.encode(apReq)
        )

        sessionSetup["Parameters"]["SecurityBlobLength"] = len(blob)
        sessionSetup["Parameters"].getData()
        sessionSetup["Data"]["SecurityBlob"] = blob.getData()

        # Fake Data here, don't want to get us fingerprinted
        sessionSetup["Data"]["NativeOS"] = "Unix"
        sessionSetup["Data"]["NativeLanMan"] = "Samba"

        smb.addCommand(sessionSetup)
        self.sendSMB(smb)

        smb = self.recvSMB()
        if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX):
            # We will need to use this uid field for all future requests/responses
            self._uid = smb["Uid"]

            # Now we have to extract the blob to continue the auth process
            sessionResponse = SMBCommand(smb["Data"][0])
            sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse["Parameters"])
            sessionData = SMBSessionSetupAndX_Extended_Response_Data(flags=smb["Flags2"])
            sessionData["SecurityBlobLength"] = sessionParameters["SecurityBlobLength"]
            sessionData.fromString(sessionResponse["Data"])

            self._action = sessionParameters["Action"]
            # If smb sign required, let's enable it for the rest of the connection
            if self._dialects_parameters["SecurityMode"] & SMB.SECURITY_SIGNATURES_REQUIRED:
                self._SigningSessionKey = sessionKey.contents
                self._SignSequenceNumber = 2
                self._SignatureEnabled = True

            # restore unicode flag if needed
            if flags2 & SMB.FLAGS2_UNICODE:
                self.__flags2 |= SMB.FLAGS2_UNICODE

            return 1
        else:
            raise Exception("Error: Could not login successfully")

    def login_extended(self, user, password, domain="", lmhash="", nthash="", use_ntlmv2=True):

        # login feature does not support unicode
        # disable it if enabled
        flags2 = self.__flags2
        if flags2 & SMB.FLAGS2_UNICODE:
            self.__flags2 = flags2 & (flags2 ^ SMB.FLAGS2_UNICODE)

        # Once everything's working we should join login methods into a single one
        smb = NewSMBPacket()
        # Are we required to sign SMB? If so we do it, if not we skip it
        if self._SignatureRequired:
            smb["Flags2"] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE

        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
        sessionSetup["Parameters"] = SMBSessionSetupAndX_Extended_Parameters()
        sessionSetup["Data"] = SMBSessionSetupAndX_Extended_Data()

        sessionSetup["Parameters"]["MaxBufferSize"] = 61440
        sessionSetup["Parameters"]["MaxMpxCount"] = 2
        sessionSetup["Parameters"]["VcNumber"] = 1
        sessionSetup["Parameters"]["SessionKey"] = 0
        sessionSetup["Parameters"]["Capabilities"] = (
            SMB.CAP_EXTENDED_SECURITY
            | SMB.CAP_USE_NT_ERRORS
            | SMB.CAP_UNICODE
            | SMB.CAP_LARGE_READX
            | SMB.CAP_LARGE_WRITEX
        )

        # Let's build a NegTokenInit with the NTLMSSP
        # TODO: In the future we should be able to choose different providers

        blob = SPNEGO_NegTokenInit()

        # NTLMSSP
        blob["MechTypes"] = [TypesMech["NTLMSSP - Microsoft NTLM Security Support Provider"]]
        auth = ntlm.getNTLMSSPType1(self.get_client_name(), domain, self._SignatureRequired, use_ntlmv2=use_ntlmv2)
        blob["MechToken"] = auth.getData()

        sessionSetup["Parameters"]["SecurityBlobLength"] = len(blob)
        sessionSetup["Parameters"].getData()
        sessionSetup["Data"]["SecurityBlob"] = blob.getData()

        # Fake Data here, don't want to get us fingerprinted
        sessionSetup["Data"]["NativeOS"] = "Unix"
        sessionSetup["Data"]["NativeLanMan"] = "Samba"

        smb.addCommand(sessionSetup)
        self.sendSMB(smb)

        smb = self.recvSMB()
        if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX):
            # We will need to use this uid field for all future requests/responses
            self._uid = smb["Uid"]

            # Now we have to extract the blob to continue the auth process
            sessionResponse = SMBCommand(smb["Data"][0])
            sessionParameters = SMBSessionSetupAndX_Extended_Response_Parameters(sessionResponse["Parameters"])
            sessionData = SMBSessionSetupAndX_Extended_Response_Data(flags=smb["Flags2"])
            sessionData["SecurityBlobLength"] = sessionParameters["SecurityBlobLength"]
            sessionData.fromString(sessionResponse["Data"])
            respToken = SPNEGO_NegTokenResp(sessionData["SecurityBlob"])

            # Let's parse some data and keep it to ourselves in case it is asked
            ntlmChallenge = ntlm.NTLMAuthChallenge(respToken["ResponseToken"])
            if ntlmChallenge["TargetInfoFields_len"] > 0:
                av_pairs = ntlm.AV_PAIRS(ntlmChallenge["TargetInfoFields"][: ntlmChallenge["TargetInfoFields_len"]])
                if av_pairs[ntlm.NTLMSSP_AV_HOSTNAME] is not None:
                    try:
                        self.__server_name = av_pairs[ntlm.NTLMSSP_AV_HOSTNAME][1].decode("utf-16le")
                    except UnicodeDecodeError:
                        # For some reason, we couldn't decode Unicode here.. silently discard the operation
                        pass
                if av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME] is not None:
                    try:
                        if self.__server_name != av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode("utf-16le"):
                            self.__server_domain = av_pairs[ntlm.NTLMSSP_AV_DOMAINNAME][1].decode("utf-16le")
                    except UnicodeDecodeError:
                        # For some reason, we couldn't decode Unicode here.. silently discard the operation
                        pass
                if av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME] is not None:
                    try:
                        self.__server_dns_domain_name = av_pairs[ntlm.NTLMSSP_AV_DNS_DOMAINNAME][1].decode("utf-16le")
                    except UnicodeDecodeError:
                        # For some reason, we couldn't decode Unicode here.. silently discard the operation
                        pass

                if av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME] is not None:
                    try:
                        self.__server_dns_host_name = av_pairs[ntlm.NTLMSSP_AV_DNS_HOSTNAME][1].decode("utf-16le")
                    except UnicodeDecodeError:
                        # For some reason, we couldn't decode Unicode here.. silently discard the operation
                        pass

            if self._strict_hostname_validation:
                self.perform_hostname_validation()

            # Parse Version to know the target Operating system name. Not provided elsewhere anymore
            if "Version" in ntlmChallenge.fields:
                version = ntlmChallenge["Version"]

                if len(version) >= 4:
                    self.__server_os_major, self.__server_os_minor, self.__server_os_build = unpack("<BBH", version[:4])

            type3, exportedSessionKey = ntlm.getNTLMSSPType3(
                auth, respToken["ResponseToken"], user, password, domain, lmhash, nthash, use_ntlmv2=use_ntlmv2
            )

            if exportedSessionKey is not None:
                self._SigningSessionKey = exportedSessionKey

            smb = NewSMBPacket()

            # Are we required to sign SMB? If so we do it, if not we skip it
            if self._SignatureRequired:
                smb["Flags2"] |= SMB.FLAGS2_SMB_SECURITY_SIGNATURE

            respToken2 = SPNEGO_NegTokenResp()
            respToken2["ResponseToken"] = type3.getData()

            # Reusing the previous structure
            sessionSetup["Parameters"]["SecurityBlobLength"] = len(respToken2)
            sessionSetup["Data"]["SecurityBlob"] = respToken2.getData()

            # Storing some info for later use
            self.__server_os = sessionData["NativeOS"]
            self.__server_lanman = sessionData["NativeLanMan"]

            smb.addCommand(sessionSetup)
            self.sendSMB(smb)

            smb = self.recvSMB()
            self._uid = 0
            if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX):
                self._uid = smb["Uid"]
                sessionResponse = SMBCommand(smb["Data"][0])
                sessionParameters = SMBSessionSetupAndXResponse_Parameters(sessionResponse["Parameters"])

                self._action = sessionParameters["Action"]
                # If smb sign required, let's enable it for the rest of the connection
                if self._dialects_parameters["SecurityMode"] & SMB.SECURITY_SIGNATURES_REQUIRED:
                    self._SignSequenceNumber = 2
                    self._SignatureEnabled = True

                # restore unicode flag if needed
                if flags2 & SMB.FLAGS2_UNICODE:
                    self.__flags2 |= SMB.FLAGS2_UNICODE

                return 1
        else:
            raise Exception("Error: Could not login successfully")

    def getCredentials(self):
        return (
            self.__userName,
            self.__password,
            self.__domain,
            self.__lmhash,
            self.__nthash,
            self.__aesKey,
            self.__TGT,
            self.__TGS,
        )

    def getIOCapabilities(self):
        res = dict()
        if (self._dialects_parameters["Capabilities"] & SMB.CAP_LARGE_READX) and self._SignatureEnabled is False:
            max_size = 65000
        else:
            max_size = self._dialects_parameters["MaxBufferSize"]  # Read in multiple KB blocks
        res["MaxReadSize"] = max_size
        res["MaxWriteSize"] = max_size
        return res

    def login(self, user, password, domain="", lmhash="", nthash="", ntlm_fallback=True):

        # If we have hashes, normalize them
        if lmhash != "" or nthash != "":
            if len(lmhash) % 2:
                lmhash = "0%s" % lmhash
            if len(nthash) % 2:
                nthash = "0%s" % nthash
            try:  # just in case they were converted already
                lmhash = a2b_hex(lmhash)
                nthash = a2b_hex(nthash)
            except:
                pass

        self.__userName = user
        self.__password = password
        self.__domain = domain
        self.__lmhash = lmhash
        self.__nthash = nthash
        self.__aesKey = ""
        self.__TGT = None
        self.__TGS = None

        if self._dialects_parameters["Capabilities"] & SMB.CAP_EXTENDED_SECURITY:
            try:
                self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2=True)
            except:
                # If the target OS is Windows 5.0 or Samba, let's try using NTLMv1
                if ntlm_fallback and (
                    (self.get_server_lanman().find("Windows 2000") != -1)
                    or (self.get_server_lanman().find("Samba") != -1)
                ):
                    self.login_extended(user, password, domain, lmhash, nthash, use_ntlmv2=False)
                    self.__isNTLMv2 = False
                else:
                    raise
        elif ntlm_fallback:
            self.login_standard(user, password, domain, lmhash, nthash)
            self.__isNTLMv2 = False
        else:
            raise SessionError("Cannot authenticate against target, enable ntlm_fallback")

    def login_standard(self, user, password, domain="", lmhash="", nthash=""):

        # login feature does not support unicode
        # disable it if enabled
        flags2 = self.__flags2
        if flags2 & SMB.FLAGS2_UNICODE:
            self.__flags2 = flags2 & (flags2 ^ SMB.FLAGS2_UNICODE)

        # Only supports NTLMv1
        # Password is only encrypted if the server passed us an "encryption key" during protocol dialect negotiation
        if self._dialects_parameters["ChallengeLength"] > 0:
            if lmhash != "" or nthash != "":
                pwd_ansi = self.get_ntlmv1_response(lmhash)
                pwd_unicode = self.get_ntlmv1_response(nthash)
            elif password:
                lmhash = ntlm.compute_lmhash(password)
                nthash = ntlm.compute_nthash(password)
                pwd_ansi = self.get_ntlmv1_response(lmhash)
                pwd_unicode = self.get_ntlmv1_response(nthash)
            else:  # NULL SESSION
                pwd_ansi = ""
                pwd_unicode = ""
        else:
            pwd_ansi = password
            pwd_unicode = ""

        smb = NewSMBPacket()

        sessionSetup = SMBCommand(SMB.SMB_COM_SESSION_SETUP_ANDX)
        sessionSetup["Parameters"] = SMBSessionSetupAndX_Parameters()
        sessionSetup["Data"] = SMBSessionSetupAndX_Data()

        sessionSetup["Parameters"]["MaxBuffer"] = 61440
        sessionSetup["Parameters"]["MaxMpxCount"] = 2
        sessionSetup["Parameters"]["VCNumber"] = os.getpid() & 0xFFFF  # Value has to be expressed in 2 bytes
        sessionSetup["Parameters"]["SessionKey"] = self._dialects_parameters["SessionKey"]
        sessionSetup["Parameters"]["AnsiPwdLength"] = len(pwd_ansi)
        sessionSetup["Parameters"]["UnicodePwdLength"] = len(pwd_unicode)
        sessionSetup["Parameters"]["Capabilities"] = (
            SMB.CAP_RAW_MODE | SMB.CAP_USE_NT_ERRORS | SMB.CAP_LARGE_READX | SMB.CAP_LARGE_WRITEX
        )

        sessionSetup["Data"]["AnsiPwd"] = pwd_ansi
        sessionSetup["Data"]["UnicodePwd"] = pwd_unicode
        sessionSetup["Data"]["Account"] = str(user)
        sessionSetup["Data"]["PrimaryDomain"] = str(domain)
        sessionSetup["Data"]["NativeOS"] = str(os.name)
        sessionSetup["Data"]["NativeLanMan"] = "pysmb"
        smb.addCommand(sessionSetup)

        self.sendSMB(smb)

        smb = self.recvSMB()
        if smb.isValidAnswer(SMB.SMB_COM_SESSION_SETUP_ANDX):
            # We will need to use this uid field for all future requests/responses
            self._uid = smb["Uid"]
            sessionResponse = SMBCommand(smb["Data"][0])
            sessionParameters = SMBSessionSetupAndXResponse_Parameters(sessionResponse["Parameters"])
            sessionData = SMBSessionSetupAndXResponse_Data(flags=smb["Flags2"], data=sessionResponse["Data"])

            self._action = sessionParameters["Action"]

            # Still gotta figure out how to do this with no EXTENDED_SECURITY
            if sessionParameters["Action"] & SMB_SETUP_USE_LANMAN_KEY == 0:
                self._SigningChallengeResponse = sessionSetup["Data"]["UnicodePwd"]
                self._SigningSessionKey = nthash
            else:
                self._SigningChallengeResponse = sessionSetup["Data"]["AnsiPwd"]
                self._SigningSessionKey = lmhash

            # self._SignSequenceNumber = 1
            # self.checkSignSMB(smb, self._SigningSessionKey ,self._SigningChallengeResponse)
            # self._SignatureEnabled = True
            self.__server_os = sessionData["NativeOS"]
            self.__server_lanman = sessionData["NativeLanMan"]
            self.__server_domain = sessionData["PrimaryDomain"]

            # restore unicode flag if needed
            if flags2 & SMB.FLAGS2_UNICODE:
                self.__flags2 |= SMB.FLAGS2_UNICODE

            return 1
        else:
            raise Exception("Error: Could not login successfully")

    def waitNamedPipe(self, tid, pipe, timeout=5, noAnswer=0):
        smb = NewSMBPacket()
        smb["Tid"] = tid

        transCommand = SMBCommand(SMB.SMB_COM_TRANSACTION)
        transCommand["Parameters"] = SMBTransaction_Parameters()
        transCommand["Data"] = SMBTransaction_Data()

        setup = "\x53\x00\x00\x00"
        name = "\\PIPE%s\x00" % pipe
        transCommand["Parameters"]["Setup"] = setup
        transCommand["Parameters"]["TotalParameterCount"] = 0
        transCommand["Parameters"]["TotalDataCount"] = 0
        transCommand["Parameters"]["MaxParameterCount"] = 0
        transCommand["Parameters"]["MaxDataCount"] = 0
        transCommand["Parameters"]["Timeout"] = timeout * 1000

        transCommand["Parameters"]["ParameterCount"] = 0
        transCommand["Parameters"]["ParameterOffset"] = 32 + 3 + 28 + len(setup) + len(name)

        transCommand["Parameters"]["DataCount"] = 0
        transCommand["Parameters"]["DataOffset"] = 0

        transCommand["Data"]["Name"] = name
        transCommand["Data"]["Trans_Parameters"] = ""
        transCommand["Data"]["Trans_Data"] = ""

        if noAnswer:
            transCommand["Parameters"]["Flags"] = TRANS_NO_RESPONSE

        smb.addCommand(transCommand)
        self.sendSMB(smb)

        smb = self.recvSMB()
        if smb.isValidAnswer(SMB.SMB_COM_TRANSACTION):
            return 1
        return 0

    def read(self, tid, fid, offset=0, max_size=None, wait_answer=1):
        if not max_size:
            max_size = self._dialects_parameters["MaxBufferSize"]  # Read in multiple KB blocks

        # max_size is not working, because although it would, the server returns an error (More data avail)

        smb = NewSMBPacket()
        smb["Tid"] = tid

        read = SMBCommand(SMB.SMB_COM_READ)
        read["Parameters"] = SMBRead_Parameters()
        read["Parameters"]["Fid"] = fid
        read["Parameters"]["Offset"] = offset
        read["Parameters"]["Count"] = max_size
        smb.addCommand(read)

        if wait_answer:
            while 1:
                self.sendSMB(smb)
                ans = self.recvSMB()

                if ans.isValidAnswer(SMB.SMB_COM_READ):
                    readResponse = SMBCommand(ans["Data"][0])
                    readData = SMBReadResponse_Data(readResponse["Data"])

                    return readData["Data"]

        return None

    def read_andx(self, tid, fid, offset=0, max_size=None, wait_answer=1, smb_packet=None):
        if not max_size:
            if (self._dialects_parameters["Capabilities"] & SMB.CAP_LARGE_READX) and self._SignatureEnabled is False:
                max_size = 65000
            else:
                max_size = self._dialects_parameters["MaxBufferSize"]  # Read in multiple KB blocks

        # max_size is not working, because although it would, the server returns an error (More data avail)

        if smb_packet is None:
            smb = NewSMBPacket()
            smb["Tid"] = tid

            readAndX = SMBCommand(SMB.SMB_COM_READ_ANDX)
            readAndX["Parameters"] = SMBReadAndX_Parameters()
            readAndX["Parameters"]["Fid"] = fid
            readAndX["Parameters"]["Offset"] = offset
            readAndX["Parameters"]["MaxCount"] = max_size
            smb.addCommand(readAndX)
        else:
            smb = smb_packet

        if wait_answer:
            answer = b""
            while 1:
                self.sendSMB(smb)
                ans = self.recvSMB()

                if ans.isValidAnswer(SMB.SMB_COM_READ_ANDX):
                    # XXX Here we are only using a few fields from the response
                    readAndXResponse = SMBCommand(ans["Data"][0])
                    readAndXParameters = SMBReadAndXResponse_Parameters(readAndXResponse["Parameters"])

                    offset = readAndXParameters["DataOffset"]
                    count = readAndXParameters["DataCount"] + 0x10000 * readAndXParameters["DataCount_Hi"]
                    answer += ans.getData()[offset : offset + count]
                    if not ans.isMoreData():
                        return answer
                    max_size = min(max_size, readAndXParameters["Remaining"])
                    readAndX["Parameters"]["Offset"] += count  # XXX Offset is not important (apparently)
        else:
            self.sendSMB(smb)
            ans = self.recvSMB()

            try:
                if ans.isValidAnswer(SMB.SMB_COM_READ_ANDX):
                    return ans
                else:
                    return None
            except:
                return ans

        return None

    def read_raw(self, tid, fid, offset=0, max_size=None, wait_answer=1):
        if not max_size:
            max_size = self._dialects_parameters["MaxBufferSize"]  # Read in multiple KB blocks

        # max_size is not working, because although it would, the server returns an error (More data avail)
        smb = NewSMBPacket()
        smb["Tid"] = tid

        readRaw = SMBCommand(SMB.SMB_COM_READ_RAW)
        readRaw["Parameters"] = SMBReadRaw_Parameters()
        readRaw["Parameters"]["Fid"] = fid
        readRaw["Parameters"]["Offset"] = offset
        readRaw["Parameters"]["MaxCount"] = max_size
        smb.addCommand(readRaw)

        self.sendSMB(smb)
        if wait_answer:
            data = self._sess.recv_packet(self.__timeout).get_trailer()
            if not data:
                # If there is no data it means there was an error
                data = self.read_andx(tid, fid, offset, max_size)
            return data

        return None

    def write(self, tid, fid, data, offset=0, wait_answer=1):
        smb = NewSMBPacket()
        smb["Tid"] = tid

        write = SMBCommand(SMB.SMB_COM_WRITE)
        write["Parameters"] = SMBWrite_Parameters()
        write["Data"] = SMBWrite_Data()
        write["Parameters"]["Fid"] = fid
        write["Parameters"]["Count"] = len(data)
        write["Parameters"]["Offset"] = offset
        write["Parameters"]["Remaining"] = len(data)
        write["Data"]["Data"] = data
        smb.addCommand(write)

        self.sendSMB(smb)

        if wait_answer:
            smb = self.recvSMB()
            if smb.isValidAnswer(SMB.SMB_COM_WRITE):
                return smb
        return None

    def write_andx(self, tid, fid, data, offset=0, wait_answer=1, write_pipe_mode=False, smb_packet=None):
        if smb_packet is None:
            smb = NewSMBPacket()
            smb["Tid"] = tid

            writeAndX = SMBCommand(SMB.SMB_COM_WRITE_ANDX)
            smb.addCommand(writeAndX)

            writeAndX["Parameters"] = SMBWriteAndX_Parameters()
            writeAndX["Parameters"]["Fid"] = fid
            writeAndX["Parameters"]["Offset"] = offset
            writeAndX["Parameters"]["WriteMode"] = 8
            writeAndX["Parameters"]["Remaining"] = len(data)
            writeAndX["Parameters"]["DataLength"] = len(data)
            writeAndX["Parameters"]["DataOffset"] = len(smb)  # this length already includes the parameter
            writeAndX["Data"] = data

            if write_pipe_mode is True:
                # First of all we gotta know what the MaxBuffSize is
                maxBuffSize = self._dialects_parameters["MaxBufferSize"]
                if len(data) > maxBuffSize:
                    chunks_size = maxBuffSize - 60
                    writeAndX["Parameters"]["WriteMode"] = 0x0C
                    sendData = b"\xff\xff" + data
                    totalLen = len(sendData)
                    writeAndX["Parameters"]["DataLength"] = chunks_size
                    writeAndX["Parameters"]["Remaining"] = totalLen - 2
                    writeAndX["Data"] = sendData[:chunks_size]

                    self.sendSMB(smb)
                    if wait_answer:
                        smbResp = self.recvSMB()
                        smbResp.isValidAnswer(SMB.SMB_COM_WRITE_ANDX)

                    alreadySent = chunks_size
                    sendData = sendData[chunks_size:]

                    while alreadySent < totalLen:
                        writeAndX["Parameters"]["WriteMode"] = 0x04
                        writeAndX["Parameters"]["DataLength"] = len(sendData[:chunks_size])
                        writeAndX["Data"] = sendData[:chunks_size]
                        self.sendSMB(smb)
                        if wait_answer:
                            smbResp = self.recvSMB()
                            smbResp.isValidAnswer(SMB.SMB_COM_WRITE_ANDX)
                        alreadySent += writeAndX["Parameters"]["DataLength"]
                        sendData = sendData[chunks_size:]

                    return smbResp

        else:
            smb = smb_packet

        self.sendSMB(smb)

        if wait_answer:
            smb = self.recvSMB()
            if smb.isValidAnswer(SMB.SMB_COM_WRITE_ANDX):
                return smb
        return None

    def write_raw(self, tid, fid, data, offset=0, wait_answer=1):
        LOG.warning(
            "[MS-CIFS] This command was introduced in the CorePlus dialect, but is often listed as part of the LAN Manager 1.0 dialect.This command has been deprecated.Clients SHOULD use SMB_COM_WRITE_ANDX"
        )
        smb = NewSMBPacket()
        smb["Tid"] = tid

        writeRaw = SMBCommand(SMB.SMB_COM_WRITE_RAW)
        writeRaw["Parameters"] = SMBWriteRaw_Parameters()
        writeRaw["Parameters"]["Fid"] = fid
        writeRaw["Parameters"]["Offset"] = offset
        writeRaw["Parameters"]["Count"] = len(data)
        writeRaw["Parameters"]["DataLength"] = 0
        writeRaw["Parameters"]["DataOffset"] = 0
        smb.addCommand(writeRaw)

        self.sendSMB(smb)
        self._sess.send_packet(data)

        if wait_answer:
            smb = self.recvSMB()
            if smb.isValidAnswer(SMB.SMB_COM_WRITE_RAW):
                return smb
        return None

    def TransactNamedPipe(self, tid, fid, data="", noAnswer=0, waitAnswer=1, offset=0):
        self.send_trans(tid, pack("<HH", 0x26, fid), "\\PIPE\\\x00", "", data, noAnswer=noAnswer)

        if noAnswer or not waitAnswer:
            return
        smb = self.recvSMB()
        if smb.isValidAnswer(SMB.SMB_COM_TRANSACTION):
            transResponse = SMBCommand(smb["Data"][0])
            transParameters = SMBTransactionResponse_Parameters(transResponse["Parameters"])
            return transResponse["Data"][-transParameters["TotalDataCount"] :]  # Remove Potential Prefix Padding
        return None

    def TransactNamedPipeRecv(self):
        s = self.recvSMB()
        if s.isValidAnswer(SMB.SMB_COM_TRANSACTION):
            transResponse = SMBCommand(s["Data"][0])
            transParameters = SMBTransactionResponse_Parameters(transResponse["Parameters"])
            return transResponse["Data"][-transParameters["TotalDataCount"] :]  # Remove Potential Prefix Padding
        return None

    def nt_create_andx(
        self,
        tid,
        filename,
        smb_packet=None,
        cmd=None,
        shareAccessMode=FILE_SHARE_READ | FILE_SHARE_WRITE,
        disposition=FILE_OPEN,
        accessMask=0x2019F,
    ):
        filename = filename.replace("/", "\\")
        filename = filename.encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else filename

        if smb_packet is None:
            smb = NewSMBPacket()
            smb["Tid"] = tid
        else:
            smb = smb_packet

        if cmd is None:
            ntCreate = SMBCommand(SMB.SMB_COM_NT_CREATE_ANDX)
            ntCreate["Parameters"] = SMBNtCreateAndX_Parameters()
            ntCreate["Data"] = SMBNtCreateAndX_Data(flags=self.__flags2)
            ntCreate["Parameters"]["FileNameLength"] = len(filename)
            ntCreate["Parameters"]["CreateFlags"] = 0x16
            ntCreate["Parameters"]["AccessMask"] = accessMask
            ntCreate["Parameters"]["CreateOptions"] = 0x40
            ntCreate["Parameters"]["ShareAccess"] = shareAccessMode
            ntCreate["Parameters"]["Disposition"] = disposition
            ntCreate["Data"]["FileName"] = filename

            if self.__flags2 & SMB.FLAGS2_UNICODE:
                ntCreate["Data"]["Pad"] = 0x0
        else:
            ntCreate = cmd

        smb.addCommand(ntCreate)

        self.sendSMB(smb)

        while 1:
            smb = self.recvSMB()
            if smb.isValidAnswer(SMB.SMB_COM_NT_CREATE_ANDX):
                # XXX Here we are ignoring the rest of the response
                ntCreateResponse = SMBCommand(smb["Data"][0])
                ntCreateParameters = SMBNtCreateAndXResponse_Parameters(ntCreateResponse["Parameters"])

                self.fid = ntCreateParameters["Fid"]
                return ntCreateParameters["Fid"]

    def logoff(self):
        smb = NewSMBPacket()

        logOff = SMBCommand(SMB.SMB_COM_LOGOFF_ANDX)
        logOff["Parameters"] = SMBLogOffAndX()
        smb.addCommand(logOff)

        self.sendSMB(smb)
        self.recvSMB()
        # Let's clear some fields so you can login again under the same session
        self._uid = 0

    def list_path(self, service, path="*", password=None):
        path = path.replace("/", "\\")
        path = path.encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else path

        tid = self.tree_connect_andx("\\\\" + self.__remote_name + "\\" + service, password)
        try:
            findFirstParameter = SMBFindFirst2_Parameters(self.__flags2)
            findFirstParameter["SearchAttributes"] = (
                SMB_FILE_ATTRIBUTE_DIRECTORY
                | SMB_FILE_ATTRIBUTE_HIDDEN
                | SMB_FILE_ATTRIBUTE_SYSTEM
                | SMB_FILE_ATTRIBUTE_READONLY
                | SMB_FILE_ATTRIBUTE_ARCHIVE
            )
            findFirstParameter["SearchCount"] = 512
            findFirstParameter["Flags"] = SMB_FIND_RETURN_RESUME_KEYS | SMB_FIND_CLOSE_AT_EOS
            findFirstParameter["InformationLevel"] = SMB_FIND_FILE_BOTH_DIRECTORY_INFO
            findFirstParameter["SearchStorageType"] = 0
            if self.__flags2 & SMB.FLAGS2_UNICODE:
                findFirstParameter["FileName"] = path + b"\x00\x00"
            else:
                findFirstParameter["FileName"] = path + "\x00"
            self.send_trans2(tid, SMB.TRANS2_FIND_FIRST2, "\x00", findFirstParameter, "")
            files = []

            totalDataCount = 1
            findData = b""
            findFirst2ParameterBlock = b""
            while len(findData) < totalDataCount:
                resp = self.recvSMB()

                if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2):
                    trans2Response = SMBCommand(resp["Data"][0])
                    trans2Parameters = SMBTransaction2Response_Parameters(trans2Response["Parameters"])
                    totalDataCount = trans2Parameters["TotalDataCount"]
                    findFirst2ParameterBlock += trans2Response["Data"][trans2Parameters["ParameterOffset"] - 55 :][
                        : trans2Parameters["ParameterCount"]
                    ]
                    findData += trans2Response["Data"][trans2Parameters["DataOffset"] - 55 :]

            findParameterBlock = SMBFindFirst2Response_Parameters(findFirst2ParameterBlock)
            # Save the SID for resume operations
            sid = findParameterBlock["SID"]

            while True:
                record = SMBFindFileBothDirectoryInfo(data=findData)

                shortname = (
                    record["ShortName"].decode("utf-16le")
                    if self.__flags2 & SMB.FLAGS2_UNICODE
                    else record["ShortName"].decode("latin-1")
                )
                filename = (
                    record["FileName"].decode("utf-16le")
                    if self.__flags2 & SMB.FLAGS2_UNICODE
                    else record["FileName"].decode("latin-1")
                )

                fileRecord = SharedFile(
                    record["CreationTime"],
                    record["LastAccessTime"],
                    record["LastChangeTime"],
                    record["EndOfFile"],
                    record["AllocationSize"],
                    record["ExtFileAttributes"],
                    shortname,
                    filename,
                )
                files.append(fileRecord)
                if record["NextEntryOffset"] > 0 and len(findData[record["NextEntryOffset"] :]) > 0:
                    findData = findData[record["NextEntryOffset"] :]
                else:
                    # More data to search?
                    if findParameterBlock["EndOfSearch"] == 0:
                        resume_filename = record["FileName"]
                        findNextParameter = SMBFindNext2_Parameters()
                        findNextParameter["SID"] = sid
                        findNextParameter["SearchCount"] = 1024
                        findNextParameter["InformationLevel"] = SMB_FIND_FILE_BOTH_DIRECTORY_INFO
                        findNextParameter["ResumeKey"] = 0
                        findNextParameter["Flags"] = SMB_FIND_RETURN_RESUME_KEYS | SMB_FIND_CLOSE_AT_EOS
                        if self.__flags2 & SMB.FLAGS2_UNICODE:
                            findNextParameter["FileName"] = resume_filename + b"\x00\x00"
                        else:
                            findNextParameter["FileName"] = resume_filename + b"\x00"
                        self.send_trans2(tid, SMB.TRANS2_FIND_NEXT2, "\x00", findNextParameter, "")
                        findData = b""
                        findNext2ParameterBlock = b""
                        totalDataCount = 1
                        while len(findData) < totalDataCount:
                            resp = self.recvSMB()

                            if resp.isValidAnswer(SMB.SMB_COM_TRANSACTION2):
                                trans2Response = SMBCommand(resp["Data"][0])
                                trans2Parameters = SMBTransaction2Response_Parameters(trans2Response["Parameters"])
                                totalDataCount = trans2Parameters["TotalDataCount"]
                                findNext2ParameterBlock += trans2Response["Data"][
                                    trans2Parameters["ParameterOffset"] - 55 :
                                ][: trans2Parameters["ParameterCount"]]
                                findData += trans2Response["Data"][trans2Parameters["DataOffset"] - 55 :]
                                findParameterBlock = SMBFindNext2Response_Parameters(findNext2ParameterBlock)
                    else:
                        break
        finally:
            self.disconnect_tree(tid)

        return files

    def retr_file(
        self, service, filename, callback, mode=FILE_OPEN, offset=0, password=None, shareAccessMode=SMB_ACCESS_READ
    ):
        filename = filename.replace("/", "\\")

        fid = -1
        tid = self.tree_connect_andx("\\\\" + self.__remote_name + "\\" + service, password)
        try:
            fid = self.nt_create_andx(tid, filename, shareAccessMode=shareAccessMode, accessMask=0x20089)

            res = self.query_file_info(tid, fid)
            datasize = SMBQueryFileStandardInfo(res)["EndOfFile"]

            self.__nonraw_retr_file(tid, fid, offset, datasize, callback)
        finally:
            if fid >= 0:
                self.close(tid, fid)
            self.disconnect_tree(tid)

    def stor_file(
        self,
        service,
        filename,
        callback,
        mode=FILE_OVERWRITE_IF,
        offset=0,
        password=None,
        shareAccessMode=SMB_ACCESS_WRITE,
    ):
        filename = filename.replace("/", "\\")

        fid = -1
        tid = self.tree_connect_andx("\\\\" + self.__remote_name + "\\" + service, password)
        try:
            fid = self.nt_create_andx(tid, filename, shareAccessMode=shareAccessMode, disposition=mode)

            self.__nonraw_stor_file(tid, fid, offset, 0, callback)
        finally:
            if fid >= 0:
                self.close(tid, fid)
            self.disconnect_tree(tid)

    def stor_file_nonraw(
        self,
        service,
        filename,
        callback,
        mode=FILE_OVERWRITE_IF,
        offset=0,
        password=None,
        shareAccessMode=SMB_ACCESS_WRITE,
    ):
        filename = filename.replace("/", "\\")

        fid = -1
        tid = self.tree_connect_andx("\\\\" + self.__remote_name + "\\" + service, password)
        try:
            fid = self.nt_create_andx(tid, filename, shareAccessMode=shareAccessMode, disposition=mode)
            self.__nonraw_stor_file(tid, fid, offset, 0, callback)
        finally:
            if fid >= 0:
                self.close(tid, fid)
            self.disconnect_tree(tid)

    def check_dir(self, service, path, password=None):
        path = path.replace("/", "\\")
        tid = self.tree_connect_andx("\\\\" + self.__remote_name + "\\" + service, password)
        try:
            smb = NewSMBPacket()
            smb["Tid"] = tid
            smb["Mid"] = 0

            cmd = SMBCommand(SMB.SMB_COM_CHECK_DIRECTORY)
            cmd["Parameters"] = ""
            cmd["Data"] = SMBCheckDirectory_Data(flags=self.__flags2)
            cmd["Data"]["DirectoryName"] = path.encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else path
            smb.addCommand(cmd)

            self.sendSMB(smb)

            while 1:
                s = self.recvSMB()
                if s.isValidAnswer(SMB.SMB_COM_CHECK_DIRECTORY):
                    return
        finally:
            self.disconnect_tree(tid)

    def remove(self, service, path, password=None):
        path = path.replace("/", "\\")
        # Perform a list to ensure the path exists
        self.list_path(service, path, password)

        tid = self.tree_connect_andx("\\\\" + self.__remote_name + "\\" + service, password)
        try:
            smb = NewSMBPacket()
            smb["Tid"] = tid
            smb["Mid"] = 0

            cmd = SMBCommand(SMB.SMB_COM_DELETE)
            cmd["Parameters"] = SMBDelete_Parameters()
            cmd["Parameters"]["SearchAttributes"] = ATTR_HIDDEN | ATTR_SYSTEM | ATTR_ARCHIVE
            cmd["Data"] = SMBDelete_Data(flags=self.__flags2)
            cmd["Data"]["FileName"] = (
                (path + "\x00").encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else (path + "\x00")
            )
            smb.addCommand(cmd)

            self.sendSMB(smb)

            while 1:
                s = self.recvSMB()
                if s.isValidAnswer(SMB.SMB_COM_DELETE):
                    return
        finally:
            self.disconnect_tree(tid)

    def rmdir(self, service, path, password=None):
        path = path.replace("/", "\\")
        # Check that the directory exists
        self.check_dir(service, path, password)

        tid = self.tree_connect_andx("\\\\" + self.__remote_name + "\\" + service, password)
        try:
            path = path.encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else path

            smb = NewSMBPacket()
            smb["Tid"] = tid
            createDir = SMBCommand(SMB.SMB_COM_DELETE_DIRECTORY)
            createDir["Data"] = SMBDeleteDirectory_Data(flags=self.__flags2)
            createDir["Data"]["DirectoryName"] = path
            smb.addCommand(createDir)

            self.sendSMB(smb)

            while 1:
                s = self.recvSMB()
                if s.isValidAnswer(SMB.SMB_COM_DELETE_DIRECTORY):
                    return
        finally:
            self.disconnect_tree(tid)

    def mkdir(self, service, path, password=None):
        path = path.replace("/", "\\")
        tid = self.tree_connect_andx("\\\\" + self.__remote_name + "\\" + service, password)
        try:
            path = path.encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else path

            smb = NewSMBPacket()
            smb["Tid"] = tid
            smb["Mid"] = 0

            createDir = SMBCommand(SMB.SMB_COM_CREATE_DIRECTORY)
            createDir["Data"] = SMBCreateDirectory_Data(flags=self.__flags2)
            createDir["Data"]["DirectoryName"] = path
            smb.addCommand(createDir)

            self.sendSMB(smb)

            smb = self.recvSMB()
            if smb.isValidAnswer(SMB.SMB_COM_CREATE_DIRECTORY):
                return 1
            return 0
        finally:
            self.disconnect_tree(tid)

    def rename(self, service, old_path, new_path, password=None):
        old_path = old_path.replace("/", "\\")
        new_path = new_path.replace("/", "\\")
        tid = self.tree_connect_andx("\\\\" + self.__remote_name + "\\" + service, password)
        try:
            smb = NewSMBPacket()
            smb["Tid"] = tid
            smb["Mid"] = 0

            renameCmd = SMBCommand(SMB.SMB_COM_RENAME)
            renameCmd["Parameters"] = SMBRename_Parameters()
            renameCmd["Parameters"]["SearchAttributes"] = ATTR_SYSTEM | ATTR_HIDDEN | ATTR_DIRECTORY
            renameCmd["Data"] = SMBRename_Data(flags=self.__flags2)
            renameCmd["Data"]["OldFileName"] = (
                old_path.encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else old_path
            )
            renameCmd["Data"]["NewFileName"] = (
                new_path.encode("utf-16le") if self.__flags2 & SMB.FLAGS2_UNICODE else new_path
            )
            smb.addCommand(renameCmd)

            self.sendSMB(smb)

            smb = self.recvSMB()
            if smb.isValidAnswer(SMB.SMB_COM_RENAME):
                return 1
            return 0
        finally:
            self.disconnect_tree(tid)

    def writeFile(self, treeId, fileId, data, offset=0):
        if (self._dialects_parameters["Capabilities"] & SMB.CAP_LARGE_WRITEX) and self._SignatureEnabled is False:
            max_buf_size = 65000
        else:
            max_buf_size = self._dialects_parameters["MaxBufferSize"] & ~0x3FF  # Write in multiple KB blocks

        write_offset = offset
        while 1:
            if len(data) == 0:
                break
            writeData = data[:max_buf_size]
            data = data[max_buf_size:]

            smb = self.write_andx(treeId, fileId, writeData, write_offset)
            writeResponse = SMBCommand(smb["Data"][0])
            writeResponseParameters = SMBWriteAndXResponse_Parameters(writeResponse["Parameters"])
            write_offset += writeResponseParameters["Count"]

    def get_socket(self):
        return self._sess.get_socket()

    def send_nt_trans(self, tid, subcommand, max_param_count, setup="", param="", data=""):
        """
        [MS-CIFS]: 2.2.4.62.1 SMB_COM_NT_TRANSACT request.
        :param tid:
        :param subcommand: The transaction subcommand code
        :param max_param_count:  This field MUST be set as specified in the subsections of Transaction subcommands.
        :param setup: Transaction context to the server, depends on transaction subcommand.
        :param param: Subcommand parameter bytes if any, depends on transaction subcommand.
        :param data: Subcommand data bytes if any, depends on transaction subcommand.
        :return: Buffer relative to requested subcommand.
        """
        smb_packet = NewSMBPacket()
        smb_packet["Tid"] = tid
        #    setup depends on NT_TRANSACT subcommands so it may be 0.
        setup_bytes = pack("<H", setup) if setup != "" else ""

        transCommand = SMBCommand(SMB.SMB_COM_NT_TRANSACT)
        transCommand["Parameters"] = SMBNTTransaction_Parameters()
        transCommand["Parameters"]["MaxDataCount"] = self._dialects_parameters["MaxBufferSize"]
        transCommand["Parameters"]["Setup"] = setup_bytes
        transCommand["Parameters"]["Function"] = subcommand
        transCommand["Parameters"]["TotalParameterCount"] = len(param)
        transCommand["Parameters"]["TotalDataCount"] = len(data)
        transCommand["Parameters"]["MaxParameterCount"] = max_param_count
        transCommand["Parameters"]["MaxSetupCount"] = 0

        transCommand["Data"] = SMBNTTransaction_Data()

        # SMB header size + SMB_COM_NT_TRANSACT parameters size + length of setup bytes.
        offset = 32 + 3 + 38 + len(setup_bytes)
        transCommand["Data"]["Pad1"] = ""
        if offset % 4 != 0:
            transCommand["Data"]["Pad1"] = "\0" * (4 - offset % 4)
            offset += 4 - offset % 4  # pad1 length

        if len(param) > 0:
            transCommand["Parameters"]["ParameterOffset"] = offset
        else:
            transCommand["Parameters"]["ParameterOffset"] = 0

        offset += len(param)
        transCommand["Data"]["Pad2"] = ""
        if offset % 4 != 0:
            transCommand["Data"]["Pad2"] = "\0" * (4 - offset % 4)
            offset += 4 - offset % 4

        if len(data) > 0:
            transCommand["Parameters"]["DataOffset"] = offset
        else:
            transCommand["Parameters"]["DataOffset"] = 0

        transCommand["Parameters"]["DataCount"] = len(data)
        transCommand["Parameters"]["ParameterCount"] = len(param)
        transCommand["Data"]["NT_Trans_Parameters"] = param
        transCommand["Data"]["NT_Trans_Data"] = data
        smb_packet.addCommand(transCommand)

        self.sendSMB(smb_packet)

    def query_sec_info(self, tid, fid, additional_information=7):
        """
        [MS-CIFS]: 2.2.7.6.1
        NT_TRANSACT_QUERY_SECURITY_DESC 0x0006
        :param tid: valid tree id.
        :param fid: valid file handle.
        :param additional_information: SecurityInfoFields. default = owner + group + dacl ie. 7
        :return: security descriptor buffer
        """
        self.send_nt_trans(
            tid, subcommand=0x0006, max_param_count=4, param=pack("<HHL", fid, 0x0000, additional_information)
        )
        resp = self.recvSMB()
        if resp.isValidAnswer(SMB.SMB_COM_NT_TRANSACT):
            nt_trans_response = SMBCommand(resp["Data"][0])
            nt_trans_parameters = SMBNTTransactionResponse_Parameters(nt_trans_response["Parameters"])
            # Remove Potential Prefix Padding
            return nt_trans_response["Data"][-nt_trans_parameters["TotalDataCount"] :]

    def echo(self, text="", count=1):

        smb = NewSMBPacket()
        comEcho = SMBCommand(SMB.SMB_COM_ECHO)
        comEcho["Parameters"] = SMBEcho_Parameters()
        comEcho["Data"] = SMBEcho_Data()
        comEcho["Parameters"]["EchoCount"] = count
        comEcho["Data"]["Data"] = text
        smb.addCommand(comEcho)

        self.sendSMB(smb)

        for i in range(count):
            resp = self.recvSMB()
            resp.isValidAnswer(SMB.SMB_COM_ECHO)
        return True


ERRDOS = {
    1: "Invalid function",
    2: "File not found",
    3: "Invalid directory",
    4: "Too many open files",
    5: "Access denied",
    6: "Invalid file handle. Please file a bug report.",
    7: "Memory control blocks destroyed",
    8: "Out of memory",
    9: "Invalid memory block address",
    10: "Invalid environment",
    11: "Invalid format",
    12: "Invalid open mode",
    13: "Invalid data",
    15: "Invalid drive",
    16: "Attempt to remove server's current directory",
    17: "Not the same device",
    18: "No files found",
    32: "Sharing mode conflicts detected",
    33: "Lock request conflicts detected",
    80: "File already exists",
}

ERRSRV = {
    1: "Non-specific error",
    2: "Bad password",
    4: "Access denied",
    5: "Invalid tid. Please file a bug report.",
    6: "Invalid network name",
    7: "Invalid device",
    49: "Print queue full",
    50: "Print queue full",
    51: "EOF on print queue dump",
    52: "Invalid print file handle",
    64: "Command not recognized. Please file a bug report.",
    65: "Internal server error",
    67: "Invalid path",
    69: "Invalid access permissions",
    71: "Invalid attribute mode",
    81: "Server is paused",
    82: "Not receiving messages",
    83: "No room to buffer messages",
    87: "Too many remote user names",
    88: "Operation timeout",
    89: "Out of resources",
    91: "Invalid user handle. Please file a bug report.",
    250: "Temporarily unable to support raw mode for transfer",
    251: "Temporarily unable to support raw mode for transfer",
    252: "Continue in MPX mode",
    65535: "Unsupported function",
}

ERRHRD = {
    19: "Media is write-protected",
    20: "Unknown unit",
    21: "Drive not ready",
    22: "Unknown command",
    23: "CRC error",
    24: "Bad request",
    25: "Seek error",
    26: "Unknown media type",
    27: "Sector not found",
    28: "Printer out of paper",
    29: "Write fault",
    30: "Read fault",
    31: "General failure",
    32: "Open conflicts with an existing open",
    33: "Invalid lock request",
    34: "Wrong disk in drive",
    35: "FCBs not available",
    36: "Sharing buffer exceeded",
}
